正则表达式入门(三)后向引用、懒惰匹配

发表于2019-11-03,长度1448, 98个单词, 4分钟读完
Flag Counter

前面介绍了分组,关于分组还有更高级的用法,叫“后向引用”。使用小括号指定一个子表达式后,匹配这个子表达式的文本(也就是此分组捕获的内容)可以在表达式或其它程序中作进一步的处理。

正则的匹配时从左向右的,所以右边叫前面,左边是后面。这不同于一般的认知:字符串的左边是前右边是后

默认情况下,每个分组会自动拥有一个组号,规则是:从左向右,以分组的左括号为标志,第一个出现的分组的组号为1,第二个为2,以此类推。例如,\1代表分组1匹配的文本。后向引用用于重复搜索前面某个分组匹配的文本。

思考这么个问题:如何匹配四段IP都一样的地址?比如1.1.1.1或255.255.255.255

如果只是用小括号后面跟大括号并不能实现,所以需要使用后向引用:

(2[0-4]\d|25[0-5]|[01]?\d\d?)\.\1\.\1\.\1

我们可以覆盖正则引擎给我们分配的组号,而使用自定义的组名。语法是(?exp)或(?'name'exp),其中exp就是组内要匹配的表达式,在其前面用问号开头,后面跟尖括号或者单引号,里面写上名称。比如上面的正则我们使用名称ent命名分组:

(?<ent>2[0-4]\d|25[0-5]|[01]?\d\d?)\.\1\.\1\.\1
或
(?<ent>2[0-4]\d|25[0-5]|[01]?\d\d?)\.\ent\.\ent\.\ent

可能你在测试过程中发现问题了:上面两个正则式,第一个可以命中,第二个不行。这是因为很多编辑器还不支持具名组,只能使用默认的组号。

另一个问题就是我既然已经分配了自定义名称,为什么组号还是可以使用的?实际上组号分配过程是要从左向右扫描两遍的:第一遍只给未命名组分配,第二遍只给命名组分配。这样实际所有命名组的组号都大于未命名的组号。可以使用(?:exp)这样的语法来剥夺一个分组对组号分配的参与权,这样就不会捕获匹配的文本了。

思考

“\10”会被解析成第10个捕获组的反向引用,还是第1个捕获组的反向引用加一个字符“0”呢? 试一下

补充:分组0对应整个正则表达式


前面我们在学习使用次数限制的时候发现,正则表达式中包含能接受重复的限定符时,通常的行为是匹配尽可能多的字符。这被称为贪婪匹配。 以这个表达式为例:a.*b,它将会匹配最长的以a开始,以b结束的字符串。用它来搜索aabab的话,它会匹配整个字符串aabab。

当需要懒惰匹配,也就是匹配尽可能少的字符,只要在次数元字符后面加上一个问号? 。前面给出的限定符都可以被转化为懒惰匹配模式。这样.?就意味着匹配任意数量的重复,但是会使用最少的重复。 a.?b匹配最短的,以a开始,以b结束的字符串。如果把它应用于aabab的话,它会匹配aab(第一到第三个字符)和ab(第四到第五个字符)。

为什么第一个匹配时aab而不是ab(扔掉第一个字符不是更短)?因为正则表达式有另一条规则,比懒惰/贪婪规则的优先级更高:最先开始的匹配拥有最高的优先权

懒惰匹配规则如下:

元字符 匹配字符
?? 最多一次,尽量没有
*? 任意次,尽可能少
+? 必须有,尽可能少
{n,m}? 重复n到m次,尽可能少
{n,}? 重复至少n次,尽可能少
Written on November 3, 2019
分类: dev, 标签: re
如果你喜欢,请赞赏! davelet