正则表达式之环顾断言(续)

3. 肯定后顾断言(positive look-behind assertion): (?<=pattern)

从当前匹配位置往后(左)看,将当前匹配位置左(后)边的字符串与子模式进行比较,若匹配则成功。
例如,要匹配Perl Hacker(而不是Java Hacker 或者Python Hacker).
即要求Hacker之前必须是单词Perl,
可以这样:

/(?<=\bPerl)\s+Hacker/

测试字符串:

He is a Perl Hacker
--------位置-------
0123456789012345678
----------1--------

可以成功匹配。我们来分析下。
首先,确定当前匹配位置。断言子模式右边的模式成功匹配的位置,
就是当前位置。后面的模式是

/\s+Hacker/

可知当前匹配位置为12.去掉其后面的字符,将剩下的字符串"He is a Perl"与断言子模式
\bPerl进行比较,结果匹配成功,于是整个模式的成功匹配。

测试字符串:

He is just another Java Hacker
-------位置-----------
012345678901234567890123456789
----------1---------2---------

则不能匹配。
忽略断言子模式,我们能看出,当前匹配位置为23.去掉位置23之后的字符,
得到"He is just another Java", 显然不能匹配断言子模式\bPerl,所以不能
匹配。

确定当前匹配位置对于分析前瞻/后顾断言很重要,我们再来分析一个稍微复杂
点的例子。
这里,我们想要匹配跟在单词a后面的Perl Hacker,
尝试模式

/\ba(?<=\bPerl)\s+Hacker/

测试字符串:

He is a Perl Hacker
--------位置-------
0123456789012345678
----------1--------

,结果匹配失败。这结果似乎有点令人吃惊,
然而它是对的。 忽略掉断言子模式,我们得到

/\ba\s+Hacker/

它要匹配的是a Hacker, "a"和"Hacker"之间只能包含空白。所以a Perl Hacker
会匹配失败。
我们需要为断言子模式预留一点位置(占位符)。尝试模式:

/\ba\s+\w+(?<=\bPerl)\s+Hacker/

忽略断言子模式,得到:

/\ba\s+\w+\s+Hacker/

这可以成功匹配。然后我们来看当前匹配位置,容易知道,当前匹配位置为12,
去掉其后面的字符,得到"He is a Perl",这可以成功匹配断言子模式\bPerl,
于是整个模式成功匹配。这里,我们是通过"\s+\w+"让正则表达式引擎先吃
掉一个单词,之后再让它回过头来看那个单词是不是Perl.

4. 否定后顾断言(negative look-behind assertion): (?<!pattern)

从当前匹配位置往后(左)看,将当前匹配位置左边的字符串与子模式进行比较,若不匹配则成功。
比如,要匹配bar前面不能是foo,即可以是xbar, mybar,但就不能是foobar,可以这样:

/(?

注意: 后顾断言中,断言子模式必须是固定宽度的。也就是说,不能使用不定宽度的量词,比如+,*,?等。
所以(?<=\w+)这样的断言是错误的。 在实际应用中,应该使用前瞻(look-ahead)断言还是后顾(look-behind)断言,取决于所选的参考点。 只有确定了参考点,才能知道该往前(右)还是往后(左)看。 比如,要匹配"后面跟着bar的foo",可以翻译为:找到参考点foo,然后向前看(look-ahead),确定右边(后)的内容是bar. 于是得到模式

/foo(?=bar)/

要匹配“前面是foo的bar”,可以翻译为: 找到参考点bar,然后往后看(look-behind),确定左边(前)的内容是foo.
于是得到模式

/(?<=foo)bar/

另外,后顾断言意味着接下来的匹配要以子模式结尾。所以/(?<=foo)bar/不能匹配foozzbar,因为它实际隐含 的意思是bar之前的东西要以foo结尾,而foozz不是以foo结尾。 同样,前瞻断言意味着接下来的匹配要以子模式开头。 所以/foo(?=bar)/不能匹配fooxxbar,因为它实际隐含 的意思是foo之后的东西要以bar开头,而xxbar不是以bar开头。

This entry was posted in Programming and tagged , . Bookmark the permalink.

Leave a Reply