正则表达式中如何匹配特定字符串中的变量值及正向先行断言示例
[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之外的任何字母。
正则进阶的内容就介绍到这里,正则表达式是一门内涵丰富的语言工具。实际上,掌握它的一些基本语法和核心概念并不算特别困难。然而,若想真正将所学运用到实践中,创作出高效的正则表达式,还有很长的一段路要走。只有那些真正对此感兴趣,并且频繁地进行研究和应用的人,才能逐渐领悟到正则表达式的深奥与精妙。现在,我就把大家带到这个阶段,接下来的学习,就靠你们自己了。
最后,也欢迎各位读者入群来交流切磋技术,戳这里: