Perl正则表达式超详细教程(9)

所以,在使用占有优先匹配模式时,它后面不应该跟其他表达式,例如a*+x永远匹配不了东西。绝大多数时候都是不会回溯的。但是少数情况下,它并非强制锁住回溯,这个和正则引擎匹配原理有关,本文不多做解释。

另外,固化分组和占有优先并不完全等价,它们只是匹配行为相同:匹配后不回溯。具体可对比后文对应内容。

perl的分组捕获和分组引用 分组的基本应用

在基础正则中,使用括号可以对匹配的内容进行分组,这种行为称为分组捕获。捕获后可以通过\1这种反向引用方式去引用(访问)保存在分组中的匹配结果。

例如:

"abc11ddabc11" =~ /([a-z]*)([0-9]*)dd\1\2/;

在perl中,还可以使用\gN的方式来反向引用分组,这个在上一节"反斜线序列"中已经解释过了。例如,以下是和上面等价的几种写法:

"abc11ddabc11" =~ /([a-z]*)([0-9]*)dd\g1\g2/; "abc11ddabc11" =~ /([a-z]*)([0-9]*)dd\g{1}\g{2}/; "abc11ddabc11" =~ /([a-z]*)([0-9]*)dd\g{-2}\g{-1}/;

perl还会把分组的内容放进perl自带的特殊变量$1,$2,...,$N中,它们和\1,\2,...\N在匹配成功时的结果上没有区别,但是\N这种类型的反向引用只在正则匹配中有效,正则匹配结束后就消亡了,而$N因为是perl的变量,即使正则已经退出匹配,也依然可以引用。所以,我们可以使用$N的方式来输出分组匹配的结果:

"abc11ddabc11" =~ /([a-z]*)([0-9]*)dd\1\2/; print "first group \\1: $1\n"; print "second group \\2: $2\n";

有两点需要注意:

这些分组可能捕获到的是空值(比如那些允许匹配0次的量词),但是整个匹配是成功的。这时候引用分组时,得到的结果也将是空值

当分组匹配失败的时候,\1会在识别括号的时候重置,而$1仍保存上一次分组成功的值

第一点,示例可知:

"abcde" =~ /([0-9]*)de/; print "null group: $1\n";

第二点,从机制上去分析。\1是每个正则匹配都相互独立的,而$1则保存分组捕获成功的值,即使这次值是上次捕获的。

这里稍微解释下正则匹配关于分组捕获的匹配过程:

例如,匹配表达式"12abc22abc" =~ /\d(abc)\d\d\1/;,当正则引擎去匹配数据时:
1.首先匹配第一个数字1,发现符合\d,于是继续用(abc)去匹配字符串,因为发现了是分组括号,于是会将第二个字符2放进分组,发现不匹配字母a,于是匹配失败,丢弃这个分组中的内容。
2.正则引擎继续向后匹配数值2,发现符合\d,于是用(abc)去匹配字符串,接着会将第三个字符a放进分组,发现能匹配,继续匹配字符串中的b、c发现都能匹配,于是分组捕获完成,将其赋值给$1,之后就能用\1和$1去引用这个分组的内容。
3.后面继续去匹配\d\d\1,直到匹配结束。

当然,具体匹配的过程不会真的这么简单,它会有一些优化匹配方式,以上只是用逻辑去描述匹配的过程。

perl中更强大的分组捕获

在perl中,支持的分组捕获更强大、更完整,它除了支持普通分组(也就是直接用括号的分组),还支持:

命名捕获(?<NAME>...):捕获后放进一个已分配好名称(即NAME)的分组中,以后可以使用这个名称来引用这个分组,如\g{NAME}引用

匿名捕获(?:...):仅分组,不捕获,所以后面无法再引用这个捕获

固化分组(?>...):一匹配成功就永不交回内容(用回溯的想法理解很容易)

匿名捕获

匿名捕获是指仅分组,不捕获。因为不捕获,所以无法使用反向引用,也不会将分组结果赋值给$1这种特殊变量。

虽然有了分组捕获功能,就可以实现任何需求,但有时候可以让这种行为变得更人性化,减少维护力度。

例如字符串"xiaofang or longshuai",使用模式/(\w+) or (\w+)/去捕获,用$1和$2分别引用or左右两个单词:

$str = "xiaofang or longshuai"; if ($str =~ /(\w+) or (\w+)/){ print "name1: $1, name2: $2\n"; }

但如果需求是中间的关系or也可以换成and,为了同时满足and和or两种需求,使用模式/(\w+) (and|or) (\w+)/去匹配,但是这时引用的序号就得由$2变为$3:

$str = "xiaofang or longshuai"; if ($str =~ /(\w+) (or|and) (\w+)/){ print "name1: $1, name2: $3\n"; }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/e1a716f6fc1f35a6ad219d4af05ea1f5.html