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

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个回答

3
(*FAIL)

或者

(*F)

使用PCRE和Perl,您可以使用此回溯控制动词,它会立即使模式匹配失败。

3
根据专业人士所说,这取决于正则表达式引擎(Regular Expression Engines),当然性能基准取决于许多因素,包括设备。
但是作为关于ECMAScript(Java / JavaScript)PCRE(PHP)的参考性能,从上到下最好的是:
  1. [] | ^[] (最快) [只使用ECMAScript]
  2. $^(非多行标志)(快速)
  3. [^\S\s] | ^[^\S\s] | ^[^\W\w] | ^[^\D\d] (快速)
  4. .^(非多行标志)(快速)
  5. (?!\x00)\x00 | ^(?!\x00)\x00 | (?!\0)\0 (快速)
  6. (?!a)a
  7. (?!) (较慢)
  8. (?=b)a (较慢)
  9. 其他示例,如\b\B等... (最慢)
一个Javascript实现的

在线尝试 (不太准确)

_注意: ^ = \A (PCRE) = 开始位置(非多行)更多信息


非常有趣,谢谢。请也添加我的这个:\A[^\w\W] - Mark
@Mark,不用谢。关于你的问题,\A(PCRE)=^(PCRE | ECMAScript),同时[\W\w]=[\S\s]=[\D\d]。所以我只是提到了它,但没有在基准测试示例中添加。 - MMMahdy-PAPION
1
谢谢@MMMahdy-PAPION,我认为\A更好,因为它在多行模式下不会匹配多次。 - Mark

1

这是对Knio的回答的微小改进:

r"\A(?!)"

这是什么意思:(?!) 表示“如果在要匹配的字符串的当前位置存在空字符串,则匹配失败。” 在正则表达式逻辑中,要匹配的字符串中到处都是空字符串:在第一个字符之前,在每对字符之间以及最后一个字符之后。 因此,(?!) 总是无法匹配。
添加 \A 可以通过防止正则表达式引擎尝试在要匹配的字符串的每个可能位置上使用 (?!) 来提高整体失败匹配的速度。与 Knio 版本的 O(字符串长度)时间相比,这个版本总是在恒定时间内匹配失败。(当然,如果您正在使用 re.match,则这不是问题,但您可能需要使用 re.search 代替...)

1

也许是这个?

/$.+^/

在Python中,这种方法仅在您控制_flags_时才有效:re.compile('$.+^', re.MULTILINE|re.DOTALL).search('a\nb\nc\n')返回一个匹配对象,对应于b和c(以及所有相邻和之间的换行符)。我推荐的负向先行断言方法适用于它可以编译的任何标志组合(即无法匹配任何内容)。 - Alex Martelli
我的错 - 把 $ 和 ^ 搞混了。 - Chris Lutz
1
这可能是试图在字符串开始之前寻找其结尾,但我发现$符号只有当它是正则表达式的最后一个字符时才表示“字符串结尾”,而且我预计类似的行为适用于^符号,因此这可能会匹配以字面上的$开头,以字面上的^结尾的子字符串。 - pavium
@pavium,Python或Javascript中它肯定不会表现出这种方式。除非你用\转义它们或者在[]字符集中包含它们,否则像$和^这样的特殊字符不应该被视为字面量。你观察到这个问题是在哪种语言中? - Peter Hansen
在Perl中,至少应该写成/\z.+\A/(参见[perldoc perlre](http://perldoc.perl.org/perlre.html#Assertions "perldoc perlre"))。这可以防止多行和单行模式(use re '/ms')对其产生影响。 - Brad Gilbert
@pavium 是正确的-这种方法作为一般的正则表达式解决方案来说是错误的。在POSIX BRE正则表达式中,“^”和“$”仅在它们分别是模式中第一个或最后一个字符时才锚定。http://pubs.opengroup.org/onlinepubs/7908799/xbd/re.html#tag_007_003_008 - phils

1

\A[^\w\W]

无论正则表达式标志如何设置,都可以使用。

根据regex101的说法:对于空输入字符串,需要0步。对于所有其他输入字符串,需要2步。

Kotlin playground: https://pl.kotl.in/hdbNH73It


0

如果不使用正则表达式,而是使用一个始终为假的if语句呢?在JavaScript中:

var willAlwaysFalse=false;
if(willAlwaysFalse)
{
}
else
{
}

我回复了Charlie的问题并添加了一条评论,解释了为什么这种方法不可取。简而言之,我需要在正则表达式中使用一个始终被使用的组,但在某些情况下,必须构建该组以确保它永远不会匹配。 - Peter Hansen

0

^_^,它永远不匹配并且快速失败。


-1
'[^0-9a-zA-Z...]*'

并用所有可打印的符号替换...。这是针对文本文件的。


我认为一定有更简短的方法,但那也是我的第一个想法^^ - F.P
4
这将匹配空字符串。要捕获每个可能的字符,请使用 [^\x00-\xFF]+(对于基于字节的实现)。 - Ferdinand Beyer
6
更好的表达应该是[^\s\S]。但正如 Ferdinand Beyer 已经说过的,它会匹配一个空字符串。 - Gumbo
4
Drakosha的正则表达式可以匹配空字符串,因为它使用了 * 通配符;如果去掉它或者换成 +,那么就必须至少匹配一个字符。如果正则表达式中的类排除了所有可能的字符,那么它就无法匹配任何内容。 - Alan Moore

-4
一个不依赖于正则表达式实现的可移植解决方案是,只需使用一个常量字符串,确保该字符串永远不会出现在日志消息中。例如,可以基于以下内容创建一个字符串:
cat /dev/urandom | hexdump | head -20
0000000 5d5d 3607 40d8 d7ab ce72 aae1 4eb3 ae47
0000010 c5e2 b9e8 910d a2d9 2eb3 fdff 6301 c85f
0000020 35d4 c282 e439 33d8 1c73 ca78 1e4d a569
0000030 8aca eb3c cbe4 aff7 d079 ca38 8831 15a5
0000040 818b 323f 0b02 caec f17f 387b 3995 88da
0000050 7b02 c80b 2d42 8087 9758 f56f b71f 0053
0000060 1501 35c9 0965 2c6e 03fe 7c6d f0ca e547
0000070 aba0 d5b6 c1d9 9bb2 fcd1 5ec7 ee9d 9963
0000080 6f0a 2c91 39c2 3587 c060 faa7 4ea4 1efd
0000090 6738 1a4c 3037 ed28 f62f 20fa 3d57 3cc0
00000a0 34f0 4bc2 3067 a1f7 9a87 086b 2876 1072
00000b0 d9e1 6b8f 5432 a60e f0f5 00b5 d9ef ed6f
00000c0 4a85 70ee 5ec4 a378 7786 927f f126 2ec2
00000d0 18c5 46fe b167 1ae6 c87c 1497 48c9 3c09
00000e0 8d09 e945 13ce 7da2 08af 1a96 c24c c022
00000f0 b051 98b3 2bf5 4d7d 5ec4 e016 a50d 355b
0000100 0e89 d9dd b153 9f0e 9a42 a51f 2d46 2435
0000110 ef35 17c2 d2aa 3cc7 e2c3 e711 d229 f108
0000120 324e 5d6a 650a d151 bc55 963f 41d3 66ee
0000130 1d8c 1fb1 1137 29b2 abf7 3af7 51fe 3cf4

当然,这不是一个智力挑战,更像是管道带编程


-8
new Regex(Guid.NewGuid().ToString())

创建一个仅包含字母数字和 '-'(其中没有正则表达式特殊字符)的模式,但同样的字符串在任何地方出现的概率是统计学上不可能的(因为这就是 GUID 的全部意义所在)。


2
“统计上不可能”?嗯?根据GUID的计算方式,预测下一个GUID是可能的,而且通常相当简单(因为它们取决于计算它们的机器和时间)。你的意思是“不太可能”,“概率非常小”,但即使对于完全随机的字符串,也不能说“不可能”。你的正则表达式将匹配无限数量的字符串——这个问题正在寻找一个不会匹配任何东西的字符串。永远不会。 - Ferdinand Beyer

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