记录perl正则表达式之递归模式的个人理解
1、perl在模式中引用一个捕获组时,这个组实际捕获的内容将用于反向引用。
/\b ( \p{alpha}+ ) \s+ \1 \b /x # numbered backref /\b ( \p{alpha}+ ) \s+ \g{1} \b /x # alternate syntax /\b ( \p{alpha}+ ) \s+ \g{–1} \b /x # relative backref /\b (?<word> \p{alpha}+ ) \s+ \k<word> \b /x # named backref
2、扩展的正则表达式,本文中涉及到的部分
扩展
原子性
含义
(? <name>...)
是
命名捕获分组
(?# ...)
否
注释,丢弃
(?: ...)
是
非捕获组
(?{...})
否
执行嵌入的perl代码
(?&name)
是
在组name上递归,其引用的是模式,而不是匹配到的内容
?1调用当前所在的组,导致引擎的递归处理,该代码是匹配平衡小括号的代码
/ ( \( (?: [^()]++ | (?1) )*+ \) )/x [^()]++表示匹配非括号,当检测到有括号时进入分支(?1),调用整个模式自身,继续深入匹配。想:用当前的模式去匹配第二条分支捕获到的内容,直到没有括号为止。
又想:如果是这样的话,那么次递归调用首先匹配的应该是‘\(’字符(左括号),那么这个模式就只能匹配一种字符串了,那就是"((((string))))",但是实际上能匹配“aa(bb(cc(dd(ee))))”这种更复杂的,所以之前的想法不对。
恩差点被正则匹配的形式忽悠了,递归递归。perl代码如下,做一个深入理解:
my $str = "aaa(s2s(rf(gt(3(eeeee)))))"; print "\nmatching!\n" if($str =~ /^ (?<p0> \w+ ) (?<pattern> (?#这里是第一个捕获组的开始) \( (?: (?<p1> [^()]++ ) (?#匹配非括号,并捕获) (?{print "< $` >|< $& >|< $' >\n";}) | (?<p2> (?&pattern) ) )*+ (?#如果前一个分支失败,递归调用<pattern>命名组) (?{ ($+{p0})?(print "p0:$+{p0}"):(print "p0:NULL"); ($+{p1})?(print "\tp1:$+{p1}"):(print "\tp1:NULL"); ($+{p2})?(print "\t\tp2:$+{p2}\n"):(print "\t\tp2:NULL\n") }) (?#打印捕获的命名组 p1 p2) (?#匹配非括号,并捕获) (?{print "< $` >|< $& >|< $' >\n";}) \) ) (?#第一个捕获组结束) $/x);
{print "< $` >|< $& >|< $' >\n";} ## $`表示匹配到的串之前的串,$&表示匹配到的串,$'表示匹配到的串之后的串
分析输出结果如下:
#######这部分可以看到正则是如何一步步递归进入的############# < >|< aaa(s2s >|< (rf(gt(3(eeeee))))) > < >|< aaa(s2s(rf >|< (gt(3(eeeee))))) > < >|< aaa(s2s(rf(gt >|< (3(eeeee))))) > < >|< aaa(s2s(rf(gt(3 >|< (eeeee))))) > < >|< aaa(s2s(rf(gt(3(eeeee >|< ))))) > #####命名组p2是每次递归返回的捕获,p1是每次递归进入之前的捕获############### p0:aaa p1:eeeee p2:NULL < >|< aaa(s2s(rf(gt(3(eeeee >|< ))))) > p0:aaa p1:3 p2:(eeeee) < >|< aaa(s2s(rf(gt(3(eeeee) >|< )))) > p0:aaa p1:gt p2:(3(eeeee)) < >|< aaa(s2s(rf(gt(3(eeeee)) >|< ))) > p0:aaa p1:rf p2:(gt(3(eeeee))) < >|< aaa(s2s(rf(gt(3(eeeee))) >|< )) > p0:aaa p1:s2s p2:(rf(gt(3(eeeee)))) < >|< aaa(s2s(rf(gt(3(eeeee)))) >|< ) > matching!