正则表达式中的“?!”操作符是如何工作的?

6

我原本以为我理解正则表达式运算符的工作原理,但现在我真的很困惑。 举个简单的例子,我有两个字符串:

mail.wow.no-1.com
mail.ololo.wow.com

我想匹配第一个,而不是第二个。 我写的正则表达式(简化版)如下:

^mail\.(.*)(?!\.wow\.com)$

当我在这两个示例上运行JS方法测试时,它只返回true(在sublime 2中,正则表达式搜索会突出显示两个字符串,这意味着两个字符串匹配)。

我知道我可以制作反向正则表达式,它将匹配第二个并根据此进行逻辑处理,但我只是想了解正则表达式中的(?!)如何工作以及我做错了什么。

谢谢。


4
http://www.regular-expressions.info/lookaround.html - Bergi
1
通常我发现负回顾后断言更容易理解,但不幸的是这是JavaScript :) - Ja͢ck
4个回答

8

你需要在预搜索中嵌入一个.*(并将其移到外部.*的前面):

^mail(?!.*\.wow\.com)\.(.*)$

否则,您的前瞻检查仅在字符串末尾进行。显然,在字符串末尾,永远不可能出现 .wow.com。现在,您可以将前瞻移动到模式的开头:
^(?!.*\.wow\.com)mail\.(.*)$
演示链接。

这种模式的效率略低,但如果影响整个字符串的前瞻都在模式的开头,我发现模式更容易阅读。


1
此外,在理解复杂的正则表达式时,您可能会发现http://tinyurl.com/m8672z9很有用。 - ForbesLindesay

3
这是一个零宽断言,也是一个负向先行断言。
它的意思是:在这个位置,接下来不能出现指定内容。
例如,(?!q) 表示当前位置后面不能跟着字母 q

3
你需要的是:
^mail\.(?!.*\.wow\.com$).*$

正如其他人所说,(?!)是一个负零宽度先行断言;它不匹配任意数量的字符,而是查看下一个字符,并确保它们不与括号中包含的内容相匹配。

Javascript从Perl复制了正则表达式语法;这些通常称为PCRE,或者Perl兼容的正则表达式;但是Javascript只有顺序环视,也就是它们从这一点开始看到未来;Perl还有负零宽度后行断言,在这种情况下会更容易处理原始示例。

# this is how it could be done in Perl
^mail\..*(?<!\.wow\.com)$

然而JavaScript选择不支持后顾断言。

1
这个URL检测程序会查找“mail.”,然后尽可能多地匹配字符,并检查是否在该字符集(即字符串末尾)和屏幕末尾之间存在“.wow.com”。
^mail\.(.*)(?!\.wow\.com)$

相反,重新排列它以检查 ".wow.com" 是否在 "mail." 之后。

^mail\.(?!.*\.wow\.com)(.*)$

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接