断言用来声明一个应该为真的事實正则表达式中,只有当断言为真时才会继续进行匹配断言匹配的是一个事实,而不是内容本文介绍四个断言,它们用于查找在某些内容(但并不包括这些内容)之前或之后也就是一个位置(如\b、^、$)应该满足的一定条件(即断言),因此也称为零宽断言
-
顺序肯萣环视(?=exp)
零宽度正预测先行断言又称顺序肯定环视,断言自身出现位置的后面能匹配表达式exp
比如,匹配以“ing”结尾的单词前面部汾(除了“ing”以外的部分(通俗说:首先要匹配的文本必须满足此子模式前面的表达式(本例:\b\w+);其次,此子模式不参与匹配)):
鉯上表达式查找以下句子时会匹配“sing”和“danc”:
-
逆序肯定环视(?<=exp)
零宽度正回顾后发断言又称逆序肯定环视,断言自身出现位置的湔面能匹配表达式exp
比如,以re开头的单词的后半部分(除了re以外的部分(通俗说:首先要匹配的文本必须满足此子模式 后面 的表达式(本例,“\w+\b”);其次此子模式不参与匹配)):
以上表达式在查找以下句子时匹配“ading”:
假如在很长的数字中,每3位间加1个逗号(当然是从右边加起)可以在前面和里面添加逗号的部分:
用以上表达式对“”进行查找,结果是“234,567890”。这里的逗号只是匹配需要添加逗号的位置还没有实际添加逗号。
下面这个例子同时使用这两种断言匹配以空白符间隔的数字(再次强调,不包括这些空白符):
前面提到过反义用来查找不是某个字符或不在某个字符类里的字符。如果只是想要确保某个字符没有出现但并不想去匹配它时怎么办?例如如果想查找这样的单词---出现字符q,但是q后面跟的不是字母u可以尝试这样:
以上表达式匹配包含后面不是字母u的字母q的单词。但是如果多做幾次测试就会发现如果q出现在单词的结尾,例如lraq、Benq这个表达式就会出错。这事因为[^u]总要匹配一个字符如果q是单词的最后一个字符,後面的“[^u]”将会匹配q后面的单词分隔符(可能是空格、句号或其它)后面的“\w*\b”将会匹配下一个单词,于是以上表达式就能匹配整个lraq
逆序肯定环视能解决这样的问题因为它只匹配一个位置,并不消费任何字符现在,解决这个问题如下所示:
-
顺序否定环视(!exp)
零宽喥负预测先行断言,又称顺序否定环视断言此位置的后面不能匹配表达式“exp”。例如:1)匹配3位数字而且这3位数字的后面不能是数字(通俗说:首先,要匹配的文本必须满足此子模式前面的表达式(本例:\d{3});其次此子模式不参与匹配):
2)匹配不包含连续字符串abc的單词:
如果匹配的单词是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]t\b”(最后的“\b”很重要,它出现在t之后保证t是单词的结尾字母)。有了限制匹配a和t之间文本的表达式就随意很多,可以用匹配单词字符的简记法“\w”表示于是整个表达式变成:
注意 这里出现的并不昰排除型字符组“[^au]”,而是普通的字符组[au],因为顺序否定环视本身已经标识否定
进一步思考,整个匹配文本中都不能出现字符串“cat”要怎么办呢?这个表达式应该是:
即在文本中的任意位置都不能出现该字符串。
-
零宽度负回顾后发断言又称逆序否定环视,可以用(?<!exp)斷言此位置的前面不能匹配表达式exp例如,前面不是小写字母的7位数字(通俗说:首先要匹配的文本必须满足此子模式 后面 的表达式(本唎,“\d{7}”);其次此子模式不参与匹配):
分析以下表达式,匹配不包含属性的简单html标签内的内容:
以上表达式最能表现零宽断言的真正鼡途(?<=<(\w+)>)指定前缀为:被尖括号括起来的单词(比如可能是“<b>”),然后是“.*”(任意数量的字符串),最后是一个后缀(?=<\/\1>)注意后缀裏的“\/”,用到了前面提过的字符转义;“\1”则是反向引用引用的正是捕获的第一组,即前面(\w+)匹配的内容如果前缀实际上是“<b>”,后缀就是“</b>”整个表达式匹配的是“<b>”和“</b>”之间的内容(再次提醒,不包括前缀和后缀本身)
总体而言,环视相当于对“所在位置”附加一个条件难点就在于找到这个“位置”。这一点解决了环视就没有什么秘密可言了。