前面我们提到过怎么查找**不是某个字符或不在某个字符类里**的字符的方法(反义)。
但是如果我们只是想要**确保某个字符没有出现,但并不想去匹配它**时怎么办?
例如,如果我们想查找这样的单词--它里面出现了字母q,但是q后面跟的不是字母u,我们可以尝试这样:
>[success]\\b\\w\*q\[^u\]\\w\*\\b
匹配包含**后面不是字母u的字母q**的单词。
但是如果多做测试(或者你思维足够敏锐,直接就观察出来了),你会发现,如果q出现在单词的结尾的话,像**Iraq**,**Benq**,这个表达式就会出错。
这是因为\[^u\]总要匹配一个字符,所以如果q是单词的最后一个字符的话,后面的\[^u\]将会匹配q后面的单词分隔符(可能是空格,或者是句号或其它的什么),后面的\\w\*\\b将会匹配下一个单词,
于是`\\b\\w\*q\[^u\]\\w\*\\b`就能匹配整个*Iraq fighting*。
**负向零宽断言**能解决这样的问题,因为它只匹配一个位置,并不**消费**任何字符。
<br/>
现在,我们可以这样来解决这个问题:\\b\\w\*q(?!u)\\w\*\\b。
**零宽度负预测先行断言**(?!exp),
断言此位置的后面不能匹配表达式exp。例如:`\\d{3}(?!\\d)`
匹配三位数字,而且这三位数字的后面不能是数字;
`\\b((?!abc)\\w)+\\b`匹配不包含连续字符串abc的单词。
<br/>
同理,我们可以用(?<!exp),
**零宽度负回顾后发断言**来断言此位置的前面不能匹配表达式
exp:(?<!\[a-z\])\\d{7}匹配前面不是小写字母的七位数字。
> 请详细分析表达式(?<=<(\\w+)>).\*(?=<\\/\\1>),这个表达式最能表现零宽断言的真正用途。
一个更复杂的例子:`(?<=<(\\w+)>).\*(?=<\\/\\1>)`
匹配不包含属性的简单HTML标签内里的内容。
(?<=<(\\w+)>)指定了这样的**前缀**:被尖括号括起来的单词(比如可能是<b>),然后是.\*(任意的字符串),最后是一个**后缀**(?=<\\/\\1>)。
注意后缀里的\\/,它用到了前面提过的字符转义;\\1则是一个反向引用,引用的正是捕获的第一组,前面的(\\w+)匹配的内容,这样如果前缀实际上是<b>的话,后缀就是</b>了。
整个表达式匹配的是<b>和</b>之间的内容(再次提醒,不包括前缀和后缀本身)。