1. 顺序肯定环视(?=exp)
零宽度正预测先行断言,又称顺序肯定环视,断言自身出现位置的后面能匹配表达式exp。
比如,匹配以“ing”结尾的单词前面部分(除了“ing”以外的部分):bw+(?=ingb)
以上表达式查找以下句子时,会匹配“sing”和“danc”:
I'm singing while you're dancing.
2. 逆序肯定环视(?<=exp)
零宽度正回顾后发断言,又称逆序肯定环视,断言自身出现位置的前面能匹配表达式exp。
比如,以re开头的单词的后半部分(除了re以外的部分):(?<=bre)w+b
以上表达式在查找以下句子时匹配“ading”:
reading a book
假如在很长的数字中,每3位间加1个逗号(当然是从右边加起),可以在前面和里面添加逗号的部分:((?<=d)d{3})+b
用以上表达式对“1234567890”进行查找,结果是“,234,567,890”。这里的逗号只是匹配需要添加逗号的位置,还没有实际添加逗号。
下面这个例子同时使用这两种断言,匹配以空白符间隔的数字(再次强调,不包括这些空白符):(?<=s)d+(?=s)
前面提到过反义,用来查找不是某个字符或不在某个字符类里的字符。如果只是想要确保某个字符没有出现,但并不想去匹配它时怎么办?例如,如果想查找这样的单词——出现字母q,但是q后面跟的不是字母u。可以尝试这样:bw*q[^u]w*b
以上表达式匹配包含后面不是字母u的字母q的单词。但是如果多做几次测试就会发现,如果q出现在单词的结尾,例如Iraq、Benq,这个表达式就会出错。这是因为[^u]总要匹配一个字符,如果q是单词的最后一个字符,后面的“[^u]”将会匹配q后面的单词分隔符(可能是空格、句号或其他),后面的“w*b”将会匹配下一个单词,于是以上表达式就能匹配整个Iraq fighting。
逆序肯定环视能解决这样的问题,因为它只匹配一个位置,并不消费任何字符。现在,解决这个问题如下所示:bw*q(?!u)w*b
3. 顺序否定环视(?!exp)
零宽度负预测先行断言,又称顺序否定环视,断言此位置的后面不能匹配表达式“exp”。例如:
1)匹配3位数字,而且这3位数字的后面不能是数字:d{3}(?!d)
2)匹配不包含连续字符串abc的单词:b((?!abc)w)+b
如果匹配的单词是c开头、t结尾,中间有一个字符,但不能是u(也就是说,整个单词不能是cut),直接用“c[^u]t”就可以了,若中间的字符不能是a或u(也就是说,整个单词不能是cat或cut),则表达式改为“c[^au]t”。
如果认真读过关于排除型字符组的章节的读者肯定会知道,这个表达式能匹配的只是cot之类的单词,因为中间的排除型字符组“[^au]”必须匹配一个字符。可是,如果还想匹配chart、conduct和court怎么办?最简单的想法是:去掉排除型字符组的长度限制,改成“c[^au]+t”。
不幸的是,这样行不通,因为这个表达式的意思是:c和t之间由多于一个“除a或u之外的字符”构成,而chart、conduct和court都包含a或u。
我们发现,其实要否定的是“单个出现的a或u”,而不仅仅是“出现的a或u”,所以才出现这样的问题。要解决这个问题,就应当把意思准确表达出来,变成“在结尾的t之前,不允许只出现一个a或u”。想到这一步,就可以用顺序否定环视(?!…)来解决。表示在这个位置向右,不允许出现子表达式能够匹配的文本,把子表达式规定为“[au]tb”(最后的“b”很重要,它出现在t之后,保证t是单词的结尾字母)。有了限制,匹配a和t之间文本的表达式就随意很多,可以用匹配单词字符的简记法“w”表示,于是整个表达式变成:c(?![au]t b)w+t
注意 这里出现的并不是排除型字符组“[^au]”,而是普通的字符组[au],因为顺序否定环视本身已经表示了否定。
进一步思考,整个匹配文本中都不能出现字符串“cat”,要怎么办呢?这个正则表达式应该是:^(?:(?!cat).)+$
即在文本中的任意位置,都不能出现该字符串。
4. 逆序否定环视(?
零宽度负回顾后发断言,又称逆序否定环视,可以用(?
分析以下表达式,匹配不包含属性的简单HTML标签内的内容:(?<=).*(?=1>)
以上表达式最能表现零宽断言的真正用途。((w+)>)指定前缀为:被尖括号括起来的单词(比如可能是“”),然后是“.*”(任意的字符串),最后是一个后缀(?=1>)。注意后缀里的“/”,用到了前面提过的字符转义;“1”则是反向引用,引用的正是捕获的第一组,即前面(w+)匹配的内容,如果前缀实际上是“”,后缀就是“”。整个表达式匹配的是“”和“”之间的内容(再次提醒,不包括前缀和后缀本身)。
总体而言,环视相当于对“所在位置”附加一个条件,难点就在于找到这个“位置”。这一点解决了,环视就没有什么秘密可言了。