ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
前面我们提到过怎么查找**不是某个字符或不在某个字符类里**的字符的方法(反义)。 但是如果我们只是想要**确保某个字符没有出现,但并不想去匹配它**时怎么办? 例如,如果我们想查找这样的单词--它里面出现了字母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>之间的内容(再次提醒,不包括前缀和后缀本身)。