一个正则表达式,永远不会与任何内容匹配

167
你有什么想法 - 一个永远不会被任何字符串匹配的正则表达式是什么样子的?
编辑:为什么我想要这个?首先,因为我觉得思考这样一个表达式很有趣,其次,因为我需要它用于一个脚本。
在那个脚本中,我将一个字典定义为Dictionary<string, Regex>。这个字典包含了一个字符串和一个表达式。
基于这个字典,我创建了一些方法,它们都只使用这个字典作为参考来完成它们的工作,其中一个方法是将正则表达式与解析后的日志文件进行匹配。
如果匹配到一个表达式,就会向另一个Dictionary<string, long>添加一个由该表达式返回的值。因此,为了捕获字典中没有匹配到表达式的任何日志消息,我创建了一个名为"unknown"的新组。
将所有没有匹配到其他任何内容的内容都添加到这个组中。但为了防止"unknown"表达式(出于偶然)与日志消息不匹配,我必须创建一个几乎肯定永远不会匹配的表达式,无论我给它什么字符串。

1
请注意,证明否定命题非常困难。 - Lasse V. Karlsen
5
有趣。你会在哪里使用这样的正则表达式? - Charlie Salts
3
我在这里记录一下,上面许多评论和对这个问题的回答最初来自于我提出的一个问题:http://stackoverflow.com/questions/1845078/what-regular-expression-can-never-match。Marc Gravell将它们合并了,我认为这使得许多回复在没有精确原始上下文的情况下有点奇怪,甚至有些评论似乎毫无意义。(可能还会夺走未来的声望分数。)我建议具有如此详细背景的问题永远不可能是“完全重复”的。无论如何... - Peter Hansen
3
此问题已添加到Stack Overflow正则表达式FAQ,位于“高级Regex-Fu”下。 - aliteralmind
7
请注意,证明否定命题非常困难——这是一个被广泛认为却显然错误的观点...因为自从欧几里得证明质数无上限以来,我们就已经知道了这一点。任何P的证明都是(not P)的证明。真正的情况是,证明经验普遍规律(包括肯定和否定的)非常困难,比如“所有乌鸦都是黑色的”或“没有乌鸦是白色的”。算法是分析性的,而不是经验性的,因此这是对虚假规则的特别恶劣的误用。例如,证明模式'a'不匹配任何以'b'开头的字符串并不是“非常困难”的问题。 - Jim Balter
显示剩余15条评论
30个回答

85

利用 负向前瞻

>>> import re
>>> x=r'(?!x)x'
>>> r=re.compile(x)
>>> r.match('')
>>> r.match('x')
>>> r.match('y')

这个正则表达式是矛盾的,因此永远不会匹配任何内容。

注意:
在Python中,re.match()隐式地在正则表达式的开头添加了一个字符串起始位置锚点(\A)。这个锚点对性能很重要:没有它,整个字符串都将被扫描。那些不使用Python的人将想要显式地添加这个锚点:

\A(?!x)x

3
似乎效果不错。但是只用“?!”呢?因为“()”总是匹配,那么“?!”就可以保证永远不匹配了吗? - Peter Hansen
2
@Peter,是的,如果Python接受该语法(最近的版本似乎是),那么它也将是自相矛盾的。另一个想法(不太优雅,但你获得的想法越多,就越有可能找到适用于所有感兴趣的RE引擎的解决方案):r'a\bc',寻找两侧都紧跟字母(变体:两侧都是非单词字符)的单词边界。 - Alex Martelli
1
有趣的是,在Python中,我的原始代码使用一个简单的文字,而这个文字在我的输入中不会出现,结果却是最快的。当使用一个5MB的输入字符串,并将其用于sub()操作时,(?!x)x的时间比原始代码长21%,(?!())长16%,($^)长6%。在某些情况下可能很重要,但在我的情况下并不重要。 - Peter Hansen
3
这段代码perl -Mre=debug -e'$_=x x 8; /(?!x)x/'执行起来可能会很慢。你可以通过在开头加上锚点\A(?!x)x或在结尾处加上锚点(?!x)x\z来加快它的速度。代码为perl -Mre=debug -e'$_=x x 8; /(?!x)x\z/; /\A(?!x)x/' - Brad Gilbert
@Brad Gilbert,事实证明Python的_re.match()_隐式地将\A添加到正则表达式的开头。正如你所指出的那样,我们其他人需要显式地添加\A - Wayne Conrad
显示剩余2条评论

79

实际上这很简单,尽管它取决于实现/标志*:

$a

会匹配字符串末尾后面的字符a。祝好运。

警告:
该表达式很耗费资源,它将扫描整个行,找到行尾锚点,然后不会找到a并返回一个负匹配。(有关更多详细信息,请参见下面的注释。)


* 最初我没有考虑到多行模式正则表达式,其中$也匹配行尾。实际上,它将匹配换行符前面的空字符串,因此普通字符a永远不会出现在$之后。


58
这个表达式很耗费时间——它需要扫描整行,找到行尾锚点,然后才会发现没有"a"并返回一个负匹配。我看它需要大约480毫秒才能扫描一个约275k的行文件。相反地,“a^”需要同样的时间,即使它可能看起来更有效率。另一方面,负向先行断言不需要扫描任何内容:“(?!x)x”(表示不跟随x的任何东西也跟随x,即什么都没有)只需大约30毫秒,即不到总时间的7%。(使用GNU Time和egrep测量。) - arantius
1
在Perl中,这将匹配$a的当前值。它的Perl等效形式$(?:a)也非常慢perl -Mre=debug -e'$_=a x 50; /$(?:a)/' - Brad Gilbert
2
在 POSIX BRE 语法中,$a 将匹配字面文本 $a,因为 $ 在该模式中无效作为锚点。 - phils
1
a^怎么办?它也不应该匹配任何东西,因为它在字符串的开头。 - Vladimir Kondenko
1
如果我没记错的话,它仍然会扫描字符串寻找 a,但是 ^o^ 应该也可以工作。 - Neil
显示剩余6条评论

68

错过的一个:

^\b$

由于空字符串不包含单词边界,因此无法匹配。在Python 2.5中进行测试。


12
这是最佳答案。它不使用前瞻,不会在某些正则表达式实现下出错,也不使用特定字符(例如 'a'),而且最多在3个处理步骤内(根据regex101.com)扫描整个输入字符串就能失败。此外,这也很容易一眼看懂。 - CubicleSoft
1
在某些情况下(如果缓冲区的开头或结尾有空行),这实际上会在Emacs中失败,但是\\\b'`有效,它替换了Emacs语法中的“文本开头/结尾”(而不是“行开头/结尾”)。 - phils
当使用MULTILINE标志时,\A\b\Z应该更具性能。 - Mark

37

四处看看:

(?=a)b

对于正则表达式新手来说,正向前瞻(?=a)确保下一个字符是a,但不更改搜索位置(或在匹配的字符串中包括'a')。现在下一个字符已被确认为a,则剩余部分的正则表达式(b)仅在下一个字符为b时匹配。因此,只有当一个字符同时为ab时,此正则表达式才匹配。


2
你的回合。 - Jack

33

a\bc,其中 \b 是一个匹配单词边界的零宽度表示。

它不能出现在单词中间,但我们却强制它出现了。


1
如果你的使用场景允许你将模式锚定到字符串开头,那么这个增强功能将防止正则表达式引擎搜索和测试文本中每个“a”的实例。 - phils

24

$.

.^

$.^

(?!)


1
太可爱了!我的潜意识让我远离像前三个那样的想法,因为它们在概念上是“非法”的...但很明显正则表达式并不这么认为。我不认识(!)这个...得去查一下。 - Peter Hansen
1
好的,我喜欢这个(?!)答案... 这基本上是Alex建议的。请注意,在https://dev59.com/53I-5IYBdhLWcg3wsKr9(由Amarghosh指出)中,有人声称“一些风格”的正则表达式会将其视为语法错误。不过Python对此表示满意。还要注意,在Python中使用re.DOTALL | re.MULTILINE模式时,您提出的其他建议都会失败。 - Peter Hansen
2
这个有被测试过吗?我本来以为 ^ 只在正则表达式的开头有特殊意义,而 $ 只在正则表达式的结尾有特殊意义,除非这个正则表达式是多行表达式。 - PP.
实际上,在Perl中,/$./的意思完全不同。它表示匹配$.(输入行号)的当前值。即使在它之前写了use re '/s';/$(.)/也可以匹配某些内容。(perl -E'say "\n" =~ /$(.)/s || 0') - Brad Gilbert
在 POSIX BRE 语法中,^$ 只有在模式的开头和结尾才是特殊字符,因此 $..^$.^ 都无法起作用。(?!) 是 Perl/PCRE 的一个特性,我认为。 - phils
$^怎么样? - undefined

16
\B\b

\b可以匹配单词边界,即字母和非字母(或字符串边界)之间的位置。
\B是它的补集 - 它匹配两个字母之间或非字母之间的位置。

它们一起无法匹配任何位置。

另请参见:


这似乎是一个非常好的解决方案,只要它锚定到特定点(文本开头似乎是合理的)。如果您不这样做,那么它就是一个糟糕的解决方案,因为将会测试文本中每个非单词边界看它是否后跟着一个单词边界!因此合理的版本应该类似于 ^\B\b。在“文本开头”和“行首”的语法不同的语言中,您需要使用“文本开头”的语法,否则您将测试每一行(例如,在Emacs中,这将是\\\B\b"\`\B\b"`)。 - phils
话虽如此,我现在注意到这个问题的陈述目的是为了获得一个正则表达式,以便在分组中使用,在某些正则表达式语法(例如 POSIX BRE)中,^ 是有问题的,因为它只是模式的第一个字符时才是锚点,否则它将匹配一个字面上的 ^ 字符。 - phils
@phils - 我认为你想太多了 :) - 这是一个非实际问题,目标是找到一个有趣的答案,而不是一个高效的答案。话虽如此,该模式可以在线性时间内拒绝(与目标字符串的大小成比例),因此对于正则表达式来说并不差 - 大多数模式都是相同的,即使 ^ 如果没有优化,也可能是线性的。 - Kobi
关于优化,我愿意忽略一个正则表达式引擎,它希望在任何其他位置找到“文本的开头” :) - phils
1
此外,这并不是一个不切实际的问答 - 我来到这里的唯一原因是想看看是否有人能够建议一个更有效的解决方案,以便实际配置特定的Emacs变量,该变量需要一个正则表达式值,但我想要有效地禁用它。 - phils

13

最大匹配

a++a

至少一个a, 后跟任意数量的a, 不会回溯。然后尝试匹配另一个a

或者独立子表达式

这等同于将a+放在独立的子表达式中,然后再跟一个a

(?>a+)a

11

Perl 5.10支持特殊的控制词,称为“verbs”,其位于(*...)序列中。(与(?...)特殊序列进行比较。) 其中包括(*FAIL)控制词,它会立即从正则表达式返回。

请注意,PCRE库中也不乏实现控制词的功能,因此您可以在使用PHP或其他语言时使用它们(但是不能在Python或Ruby中使用,因为它们使用自己的引擎)。


该网址http://perldoc.perl.org/perlre.html#%28%2AFAIL%29-%28%2AF%29的文档称:“此模式不匹配任何内容并始终失败。它等同于(?!), 但更易于阅读。实际上,(?!)在内部被优化为(*FAIL)。”有趣的是,尽管(?!)在Javascript中无法工作,但它是我迄今为止最喜欢的“纯”答案。谢谢。 - Peter Hansen

11

你觉得使用 $^ 或者是 (?!) 怎么样? 


4
在匹配模式下,使用这个表达式可以匹配换行符,同时 ^ 匹配行的开头,$ 匹配行的结尾。 - Gumbo
4
也许他的意思是 (?!) - 一个匹配空字符串的负向先行断言。但是有一些正则表达式引擎也会将其视为语法错误。 - Alan Moore
1
一个空字符串在 JavaScript 中匹配第一个,至少如此。 - Roland Pihlakas
在 POSIX BRE 语法中,$^ 将匹配这些字面字符,因为这些字符作为锚点是无效的(即您使用该模式的原因导致它不能执行您想要的操作)。 - phils

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