正则表达式中如何匹配特定字符串中的变量值及正向先行断言示例

[id_7[id_770434759]1393593]

正则表达式中如何匹配特定字符串中的变量值及正向先行断言示例

下面先来讲几种类型的断言:

正向先行断言(正前瞻)

语法:(?=pattern)

作用:匹配pattern表达式的前面内容,不返回本身。

如此表述,仍旧显得困惑不解,那么,让我们重新回到之前的例子,要想获取阅读量,在正则表达式中,这就意味着必须能够成功识别出数字前的内容。

依据所述的正面预测性断言,能够对应表达式前部的信息,换言之,使用(?:)这一结构,便能够成功捕捉到表达式前的相关内容。

匹配什么内容呢?如果要所有内容那就是:

 String reg=[id_987760176])";String test = "count">阅读数:641"; Pattern pattern = Pattern.compile(reg); Matcher mc= pattern.matcher(test); while(mc.find()){System.out.println("匹配结果:") System.out.println(mc.group()); } //匹配结果://阅读数:641

然而,兄弟们我们真正需要的只是开头的那个数字,其实操作起来并不复杂,只需将数字d进行对应匹配,那么就可以这样修改:

String reg="\d+(?=)";String test = "count">阅读数:641";Pattern pattern = Pattern.compile(reg);Matcher mc= pattern.matcher(test);while(mc.find()){ System.out.println(mc.group());}//匹配结果://641

大功告成!

正向后行断言(正后顾)

语法:(?

谈及捕获的概念,他指的是对表达式的匹配,然而,捕获这一操作往往与分组功能相辅相成,我们通常称之为“捕获组”。

捕获组负责将子表达式所对应的内容进行锁定,并将这些锁定结果存储于内存,分配给相应的数字编号或赋予特定名称。编号过程按照深度优先的原则进行,之后用户可借助编号或名称来调用这些已保存的匹配结果。

而根据命名方式的不同,又可以分为两种组。

数字编号捕获组

语法:(exp)

对表达式的解析从左侧开始,每当遇到一个左括号,并找到与之匹配的右括号,两者之间的内容便构成一个分组。其中,第0组代表整个表达式,而后续的分组则从第1组开始,依次递增。

比如固定电话的:020-85653333

他的正则表达式为:(0d{2})-(d{8})

按照左括号的顺序,这个表达式有如下分组:

请勿对编号进行修改,确保格式正确,具体如下:00(两位数字)至(八位数字)020-8565333311(两位数字)02022(八位数字)85653333。

我们用Java来验证一下:

String test = "020-85653333"; String reg="(0\d{2})-(\d{8})";Pattern对象pattern是通过调用compile方法,对正则表达式reg进行编译而创建的。创建匹配器mc,使其与模式pattern对测试字符串test进行匹配。 if(mc.find()){   System.out.println("分组的个数有:"+mc.groupCount());   for(int i=0;i<=mc.groupCount();i++){      System.out.println("第"+i+"个分组为:"+mc.group(i));   } }

输出结果:

分组的个数有:2第0个分组为:020-85653333第1个分组为:020第2个分组为:85653333

显而易见,分组数量为2,然而由于第0组实际上就是整个表达式本身,所以它也被一并呈现出来。

命名编号捕获组

语法:(?exp)

解释:分组的命名由表达式中的name指定

比如区号也可以这样写:(?d{2})-(?d{8})

该表达式按照括号内的顺序,具体分组情况如下:首先是序号名称,接着是分组内容,具体为00(0d{2}),然后是-(d{8}),再是020-856533331quhao(0d{2}),最后是0202haoma(d{8})和85653333。

用代码来验证一下:

String test = "020-85653333";String reg="(?0\d{2})-(?\d{8})";Pattern对象pattern通过调用compile方法,对正则表达式reg进行了编译处理。创建匹配器对象mc,该对象是通过将模式对象pattern与测试字符串test进行匹配操作得到的。if(mc.find()){  System.out.println("分组的个数有:"+mc.groupCount());  System.out.println(mc.group("quhao"));  System.out.println(mc.group("haoma"));}

输出结果:

分组的个数有:2分组名称为:quhao,匹配内容为:020分组名称为:haoma,匹配内容为:85653333

非捕获组

语法:(?:exp)

这种分组方式与捕获组的功能正好相反,其目的是为了标明哪些分组是不需要被捕获的,换句话说,用户可以根据自己的需求来决定哪些分组需要保留。

例如,针对上述的正则表达式,在编写程序时,若第一个括号所定义的分组并非必须使用,那么可以这样表述:可省略对前两个数字序列的捕获,即:(?:\d{2})-(\d{8})。

禁止对编号进行修改,其格式应为“00(0d{2})-(d{8})”,例如“020-8565333311(d{8})85653333”。

验证一下:

String test = "020-85653333";String reg="(?:0\d{2})-(\d{8})";Pattern对象 pattern 被创建,并执行了编译操作,用于匹配特定的文本模式。reg);Matcher mc= pattern.matcher(test);if(mc.find()){  System.out.println("分组的个数有:"+mc.groupCount());  for(inti=0;i<=mc.groupCount();i++){    System.out.println("第"+i+"个分组为:"+mc.group(i));  }}

输出结果:

分组的个数有:1第0个分组为:020-85653333第1个分组为:85653333

3、反向引用

在先前的讨论中,我们了解到:当进行捕获操作时,系统会生成一个捕获组。这个捕获组被存储在内存之中,它不仅能够在正则表达式的使用范围之外,通过程序代码被引用,还能够在正则表达式的内部被引用。这种在正则表达式内部对捕获组进行引用的方法,我们称之为反向引用。

根据捕获组的命名规则,反向引用可分为:

数字编号组反向引用:k或 umber

命名编号组反向引用:k或者'name'

好了 讲完了,懂吗?不懂!!!

可能连前面讲的捕获有什么用都还不懂吧?

其实只是看完捕获不懂不会用是很正常的!

因为捕获组通常是和反向引用一起使用的。

如前所述,捕获组能够将匹配到的子表达式内容按照顺序或名称进行保存,以便后续调用。

注意两个字眼:“内容” 和 “使用”。

这里所提及的“内容”,指的是与搜索结果相吻合的部分,而非单纯的子表达式,那么这样的强调有何意义呢?嗯,先记在心里。

那这里所说的“使用”是怎样使用呢?

此工具的主要用途在于搜寻相似信息或执行特定字符的替换操作。

还是举栗子吧。

若需寻找字符串"aabbbbgbddesddfiid"中配对的字符。

依据我们之前所学的正则表达式知识,诸如指定特定范围、设定限制条件或是使用断言等功能,可能无法实现。

现在我们先用程序思维理一下思路:

1)匹配到一个字母

2)匹配第下一个字母,检查是否和上一个字母是否一样

3)如果一样,则匹配成功,否则失败

在思路2中,当我们要匹配后续的字母时,必须依赖前一个字母的信息,那么我们该如何准确回忆起前一个字母呢?

这回捕捉到的信息派上了用场,借助它,我们能够以前一次成功匹配的内容作为此次匹配的依据。

好了,有思路就要实践

首先匹配一个字母:w

我们需要做成分组才能捕获,因此写成这样:(w)

那这个表达式就有一个捕获组:(w)

然后我们要用这个捕获组作为条件,那就可以:(w)

这样就大功告成了

可能有人不明白了,是什么意思呢?

还记得吗,捕获组有两种不同的命名方法,第一种是按照捕获分组的顺序进行命名,而第二种则是通过自定义的方式来进行命名。

在常规设置中,命名方式通常采用数字标识,并且这些数字标识的排列顺序是从第一个数字开始递增的。

因此,若需引用首个捕获组,需依照反向引用编号的命名法则,此时须使用字母k或其同义表达。

当然,通常都是是后者。

我们来测试一下:

String test = "aabbbbgbddesddfiid";Pattern pattern = Pattern.compile("(\w)\1");Matcher mc= pattern.matcher(test);while(mc.find()){输出:mc所属的分组信息}

输出结果:

aabbbbddddii

嗯,这就是我们想要的了。

在举个替换的例子,假如想要把字符串中abc换成a。

String test = "abcbbabcbcgbddesddfiid";String reg="(a)(b)c";输出到控制台的结果是:test字符串经过正则表达式reg的替换操作后,得到的新的字符串内容。"$1"));

输出结果:

abbabcgbddesddfiid

4、贪婪和非贪婪

贪婪

我们都知道,贪婪就是不满足,尽可能多的要。

在正则中,贪婪也是差不多的意思:

贪婪匹配机制:在正则表达式中,若存在允许字符重复的限定符,其常规做法是在确保整个表达式能够成功匹配的情况下,尽可能多地匹配字符。这种匹配策略被称作贪婪匹配。

该特性涉及一次性整体读取字符串进行比对,若发现不吻合,则舍弃字符串的最右侧字符,并持续进行比对,采用这种匹配与舍弃的回溯方法,直至成功匹配或整个字符串被舍弃,因此,它确保了数据的最大化返回,确保不会遗漏任何信息。

我们之前已经提及了重复限定符的概念,实际上,这些限定符等同于贪婪量词,例如,在表达式d{3,6}中即可看到这一用法。

在匹配3至6位数字的情境下,这代表了一种贪婪的匹配方式,即若字符串中存在6个数字且均符合条件,则将全部数字进行匹配。

如下面的代码。

String reg="\d{3,6}";String test="61762828 176 2991 871";System.out.println("文本:"+test);System.out.println("贪婪模式:"+reg);定义模式p1,使用compile方法对正则表达式reg进行编译。创建匹配器m1,将其设置为p1对test进行匹配。while(m1.find()){  System.out.println("匹配结果:"+m1.group(0));}

输出结果:

文本:61762828 176 2991 44 871贪婪模式:d{3,6}匹配结果:617628匹配结果:176匹配结果:2991匹配结果:871

观察结果发现,原本字符串中的“61762828”这一部分,实际上仅需三个“617”即可实现匹配,然而它并未满足这一条件,而是继续匹配,直至达到最大可匹配的字符数,即六个。

一个量词就如此贪婪了,

有人或许会质疑,当若干贪婪的量词汇集一处,它们又是如何来掌控各自的匹配优先权的呢?

如此情形,众多贪婪者齐聚一堂,若字符串能充分满足他们各自的最大匹配需求,彼此便相安无事;然而,一旦无法满足这种需求,便会依照深度优先的规则行事,即从左至右,优先满足尽可能多的贪婪量词,剩余的匹配则递归至下一个量词。

String reg="(\d{1,2})(\d{3,4})";String test="61762828 176 2991 87321";System.out.println("文本:"+test);System.out.println("贪婪模式:"+reg);Pattern p1 =Pattern.compile(reg);Matcher m1 = p1.matcher(test);while(m1.find()){  System.out.println("匹配结果:"+m1.group(0));}

输出结果:

文本:61762828 176 2991 87321贪婪模式:(d{1,2})(d{3,4})匹配结果:617628匹配结果:2991匹配结果:87321

“617628”这一序列中,前两位数字通过d{1,2}的匹配规则,成功识别出61;而后续的四位数字则通过相同的匹配规则,被识别为7628。

编号“2991”由前半部分通过d{1,2}的匹配得出了数字29,而其后半部分的匹配则产生了数字91。

数字“87321”中,前两位通过d{1,2}的规则成功对应出87,而接下来的两位则通过同样的规则对应出了321。

懒惰(非贪婪)

在正则表达式中,若存在允许重复的限定符,其常规做法是在确保整个表达式能够成功匹配的情况下,尽量减少所匹配的字符数量。这种匹配策略被称为懒惰匹配。

特性描述:在匹配过程中,从字符串的起始端开始,逐个字符地尝试匹配;若匹配成功,则终止当前匹配;若匹配失败,则继续读取下一个字符,重复此过程;如此循环,直至匹配成功或遍历完字符串中的所有字符。

懒惰量词是在贪婪量词后面加个“?”

代码说明:*表示可重复任意次数,但应尽量减少重复;+表示至少重复一次,且尽量减少重复;??表示可重复零次或一次,尽量减少重复;{n,m}表示重复n到m次,尽量减少重复;{n,}表示重复n次以上,尽量减少重复。

String reg="(\d{1,2}?)(\d{3,4})";String test="61762828 176 2991 87321";System.out.println("文本:"+test);System.out.println("贪婪模式:"+reg);Pattern p1 =Pattern.compile(reg);Matcher m1 = p1.matcher(test);while(m1.find()){  System.out.println("匹配结果:"+m1.group(0));}

输出结果:

文本:61762828 176 2991 87321贪婪模式:(d{1,2}?)(d{3,4})匹配结果:61762匹配结果:2991匹配结果:87321

“61762”这一数字由左侧的懒惰匹配得到6,而右侧的贪婪匹配则生成了1762。

"2991" 是左边的懒惰匹配出2,右边的贪婪匹配出991

"87321" 这串数字中,左侧通过懒惰匹配规则得到了数字8,而右侧则依据贪婪匹配原则得出了7321。

5、反义

如前文所述,元字符主要用于匹配特定字符,然而,若你希望进行相反操作,即排除某些字符的匹配,正则表达式同样提供了一系列常用的反义元字符供你选择。

元字符W能够识别任何非字母、非数字、非下划线、非汉字的字符;S可以识别任何非空白符的字符;D用于匹配任何非数字的字符;B表示不在单词首尾的位置;[x]代表除了x之外的任何字符;[aeiou]则匹配除aeiou之外的任何字母。

正则进阶的内容就介绍到这里,正则表达式是一门内涵丰富的语言工具。实际上,掌握它的一些基本语法和核心概念并不算特别困难。然而,若想真正将所学运用到实践中,创作出高效的正则表达式,还有很长的一段路要走。只有那些真正对此感兴趣,并且频繁地进行研究和应用的人,才能逐渐领悟到正则表达式的深奥与精妙。现在,我就把大家带到这个阶段,接下来的学习,就靠你们自己了。

最后,也欢迎各位读者入群来交流切磋技术,戳这里:

你可能想看:
« 上一篇
下一篇 »

相关推荐

FreeBasic:强大免费开源的64位basic编译器及应用优势

2025年05月10日

82阅读

脊柱侧弯矫正方法大盘点:运动康复、支具矫正等多种方式

2025年05月09日

85阅读

如何在winForm标题栏去掉标题文字且任务栏图标正常显示

2025年05月09日

85阅读

十一黄金周促销广告词大全,国庆购物狂欢等你来

2025年05月09日

77阅读

关闭快速编辑模式及插入模式,设置控制台指定文字颜色举例

2025年05月09日

63阅读

黄金搭档低糖高钙饮品包装及成分功效全解析,适合人群要知晓

2025年05月09日

91阅读