正则表达式之环顾断言

在正则表达式中,环顾断言(Look-Around Assertions)属于扩展模式,因为它是零宽度断言(zero-length assertions),所以它的结果只是匹配成功或者失败,而不会记录匹配到的东西。从结果上,环顾断言分为肯定断言(positive assertion)和否定断言(negative assertion)。肯定断言成功的前提是其子模式成功匹配,而否定断言成功的前提是其子模式不匹配。从方向上看,环顾断言又分为前瞻断言(从当前匹配位置向前/右看,look-ahead)和后顾断言(从当前匹配位置往后/左,look-behind)。这里前和后容易搞混,可以这样想,正则表达式是从左到右进行匹配的,从起点(左)出发,往前即是往右走,往后即是往左走。
综合起来,环顾断言共有以下四种情况:

1. 肯定前瞻断言(positive look-ahead assertion): (?=subpattern)

从当前匹配位置往前看,若子模式匹配,则成功。这里有一个来自《精通Perl》第二章,”高级正则表达式”的例子:匹配同时包含单词Wilma和Fred的行(不管顺序如何)。

$_ = "Here come Wilma and Fred!";
print "Matches: $_" if /(?=.*Wilma).*Fred/;

分析这个正则表达式的时候,我们首先要确定当前匹配位置。忽略断言(?=.*Wilma),于是得到模式

.*Fred

成功匹配它之后,当前匹配位置为0,也就是字符串的开头。然后再看断言子模式

.*Wilma

如果这个字符串中包含Wilma,那么匹配成功。注意,由于两个子模式的起点都是字符串的开头,所以无论是
Here come Wilma and Fred 还是 Here come Fred and Wilma,也就是说,不管Fred和Wilma的位置如何,我们的模式都能正确匹配。

2. 否定前瞻断言(negative look-ahead assertion): (?!subpattern)

从当前匹配位置往前看,若子模式不匹配,则成功。例如,要匹配单词hate后面不能是单词you的情况,可以这样

/hate(?!\s+you\b)/

这个模式可以匹配”I hate him”, “I hate your brother”, 但是不能匹配” I hate you”.
在断言前面使用量词,诸如+,?,*时,要小心,比如,若将前面的模式改为:

/hate\s+(?!you\b)/

结果将是不正确的。如果hate 后面有两个以上空格,比如”I hate  you”,它将识别不出来。贪婪量词+会首先尝试尽可能多的字符,得到当前匹配位置,在you的y左边,然后匹配断言子模式,结果是失败,接着,它会尝试少匹配(吐出)一个空格,再次匹配断言子模式,即是用” you”来匹配”you”,显然,前者多了一个空格,两者是不相等的。于是(?!you\b)成功,最终结果匹配成功,但这并不是我们想要的。
还要注意,这些断言只是向前/向后而已,他们不会更新当前匹配位置。

—未完,待续

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

Leave a Reply