一个永远不会被任何东西匹配的正则表达式

时间:2009-11-12 15:46:21

标签: regex dictionary complexity-theory

这可能听起来像是一个愚蠢的问题,但我与我的一些开发人员进行了长时间的谈话,这听起来很有趣。

因此;你的想法是什么 - 正则表达式是什么样的,永远不会被任何字符串匹配!

修改:为什么我要这个?好吧,首先是因为我觉得考虑这样的表达很有意思,其次因为我需要它来制作剧本。

在该脚本中,我将字典定义为Dictionary<string, Regex>。如您所见,它包含一个字符串和一个表达式。

基于该字典,我创建的方法都使用这个字典作为他们应该如何工作的参考,其中一个将正则表达式与已解析的日志文件相匹配。

如果表达式匹配,则为另一个Dictionary<string, long>添加表达式返回的值。因此,为了捕获与字典中的表达式不匹配的任何日志消息,我创建了一个名为“unknown”的新组。

对于此组,添加了与其他任何内容不匹配的所有内容。但是为了防止“未知”表达式(偶然)不匹配日志消息,我不得不创建一个绝对不会匹配的表达式,无论我给它什么字符串。

因此,你有理由认为这不是一个真正的问题......

27 个答案:

答案 0 :(得分:69)

利用negative lookahead

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

这个RE在术语上是矛盾的,因此永远不会匹配。

注意:
在Python中, re.match()隐式地将一个字符串开头的锚点(\A)添加到正则表达式的开头。此锚点对性能很重要:没有它,将扫描整个字符串。那些不使用Python的人会想要明确地添加锚点:

\A(?!x)x

答案 1 :(得分:60)

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

$a

在字符串结束后匹配字符a。祝你好运。

警告:
这个表达式很昂贵 - 它将扫描整行,找到行尾锚点,然后才找到a并返回否定匹配。 (有关详细信息,请参阅下面的评论。)


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

答案 2 :(得分:33)

环顾四周:

(?=a)b

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

答案 3 :(得分:30)

错过了一个:

^\b$

它无法匹配,因为空字符串不包含单词边界。在Python 2.5中测试。

答案 4 :(得分:29)

a\bc,其中\b是一个与字边界匹配的零宽度表达式。

它不能出现在一个单词的中间,我们强迫它。

答案 5 :(得分:19)

$.

.^

$.^

(?!)

答案 6 :(得分:10)

最大匹配

a++a

至少有一个a后跟任意数量的a,没有回溯。然后尝试再匹配一个a

或独立子表达式

这相当于将a+放在一个独立的子表达式中,然后是另一个a

(?>a+)a

答案 7 :(得分:10)

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

请注意,动词也会在不久之后在PCRE中实现,因此您也可以使用PCRE库在PHP或其他语言中使用它们。 (但是,你不能使用Python或Ruby。他们使用自己的引擎。)

答案 8 :(得分:9)

\B\b

\b匹配单词边界 - 字母与非字母(或字符串边界)之间的位置。
\B是它的补充 - 它匹配两个字母之间或非字母之间的位置。

他们无法匹配任何职位。

另见:

答案 9 :(得分:8)

$^(?!)怎么样?

答案 10 :(得分:8)

这似乎有效:

$.

答案 11 :(得分:4)

Python不接受它,但Perl会:

perl -ne 'print if /(w\1w)/'

这个正则表达式应该(理论上)尝试匹配无限(偶数)个w个,因为第一个组(() s)会自行处理。 Perl似乎没有发出任何警告,即使在use strict; use warnings;下,所以我认为它至少是有效的,而且我的(最小)测试无法匹配任何东西,所以我提交它以供你的批评。

答案 12 :(得分:4)

最快的将是:

r = re.compile(r'a^')
r.match('whatever')

'a'可以是任何非特殊字符('x','y')。 Knio的实现可能会更纯粹,但是对于所有字符串而言,这一点对于所有字符串都会更快,而不是以“a”开头,因为它在第一个字符后不匹配,而在第二个字符之后不匹配。

答案 13 :(得分:4)

[^\d\D](?=a)ba$aa^a

答案 14 :(得分:4)

这不适用于Python和许多其他语言,但在Javascript正则表达式中,[]是一个无法匹配的有效字符类。因此,无论输入是什么,以下内容都应立即失败:

var noMatch = /^[]/;

我比/$a/更喜欢它,因为对我来说,它清楚地传达了它的意图。至于你什么时候需要它,我需要它,因为我需要基于用户输入的动态编译模式的后备。当模式无效时,我需要用一个不匹配的模式替换它。简化,它看起来像这样:

try {
    var matchPattern = new RegExp(someUserInput);
}
catch (e) {
    matchPattern = noMatch;
}

答案 15 :(得分:2)

我相信

\Z RE FAILS! \A

甚至涵盖了正则表达式包含MULTILINE,DOTALL等标志的情况。

>>> import re
>>> x=re.compile(r"\Z RE FAILS! \A")
>>> x.match('')
>>> x.match(' RE FAILS! ')
>>>

我相信(但我没有对它进行基准测试),无论\Z\A之间的字符串长度(> 0),失败时间都应该是不变的。< / p>

答案 16 :(得分:2)

这么多好的答案!

与@nivk的答案类似,我想与Perl分享永不匹配的正则表达式的不同变体的性能比较。

  1. 输入:伪随机ascii字符串(25,000个不同的行,长度8-16):
  2. 正则表达速度:

    Total for   \A(?!x)x: 69.675450 s, 1435225 lines/s
    Total for       a\bc: 71.164469 s, 1405195 lines/s
    Total for    (?>a+)a: 71.218324 s, 1404133 lines/s
    Total for       a++a: 71.331362 s, 1401907 lines/s
    Total for         $a: 72.567302 s, 1378031 lines/s
    Total for     (?=a)b: 72.842308 s, 1372828 lines/s
    Total for     (?!x)x: 72.948911 s, 1370822 lines/s
    Total for       ^\b$: 79.417197 s, 1259173 lines/s
    Total for         $.: 88.727839 s, 1127041 lines/s
    Total for       (?!): 111.272815 s, 898692 lines/s
    Total for         .^: 115.298849 s, 867311 lines/s
    Total for    (*FAIL): 350.409864 s, 285380 lines/s
    
    1. 输入:/ usr / share / dict / words(100,000英文单词)。
    2. 正则表达速度:

      Total for   \A(?!x)x: 128.336729 s, 1564805 lines/s
      Total for     (?!x)x: 132.138544 s, 1519783 lines/s
      Total for       a++a: 133.144501 s, 1508301 lines/s
      Total for    (?>a+)a: 133.394062 s, 1505479 lines/s
      Total for       a\bc: 134.643127 s, 1491513 lines/s
      Total for     (?=a)b: 137.877110 s, 1456528 lines/s
      Total for         $a: 152.215523 s, 1319326 lines/s
      Total for       ^\b$: 153.727954 s, 1306346 lines/s
      Total for         $.: 170.780654 s, 1175906 lines/s
      Total for       (?!): 209.800379 s, 957205 lines/s
      Total for         .^: 217.943800 s, 921439 lines/s
      Total for    (*FAIL): 661.598302 s, 303540 lines/s
      

      (英特尔i5-3320M上的Ubuntu,Linux内核4.13,Perl 5.26)

答案 17 :(得分:2)

在看到其中一些很棒的答案之后,@arantius's comment(关于时间$xx^ vs (?!x)x)对当前接受的答案让我想要一些时间到目前为止给出的解决方案。

使用@ arantius的275k行标准,我在Python中运行了以下测试(v3.5.2,IPython 6.2.1)。

TL; DR:'x^''x\by'的速度最快至少约为16,与@ arantius的发现相反,(?!x)x是在最慢的中(约慢37倍)。所以速度问题肯定是依赖于实现的。如果速度对您很重要,请在提交之前在您的预期系统上进行测试。

更新:时间'x^''a^'之间显然存在很大差异。有关详细信息,请参阅this question,以及使用a代替x进行较慢时序的上一次修改。

In [1]: import re

In [2]: with open('/tmp/longfile.txt') as f:
   ...:     longfile = f.read()
   ...:     

In [3]: len(re.findall('\n',longfile))
Out[3]: 275000

In [4]: len(longfile)
Out[4]: 24733175

In [5]: for regex in ('x^','.^','$x','$.','$x^','$.^','$^','(?!x)x','(?!)','(?=x)y','(?=x)(?!x)',r'x\by',r'x\bx',r'^\b$'
    ...: ,r'\B\b',r'\ZNEVERMATCH\A',r'\Z\A'):
    ...:     print('-'*72)
    ...:     print(regex)
    ...:     %timeit re.search(regex,longfile)
    ...:     
------------------------------------------------------------------------
x^
6.98 ms ± 58.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
------------------------------------------------------------------------
.^
155 ms ± 960 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
------------------------------------------------------------------------
$x
111 ms ± 2.12 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
------------------------------------------------------------------------
$.
111 ms ± 1.76 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
------------------------------------------------------------------------
$x^
112 ms ± 1.14 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
------------------------------------------------------------------------
$.^
113 ms ± 1.44 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
------------------------------------------------------------------------
$^
111 ms ± 839 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
------------------------------------------------------------------------
(?!x)x
257 ms ± 5.03 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
------------------------------------------------------------------------
(?!)
203 ms ± 1.56 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
------------------------------------------------------------------------
(?=x)y
204 ms ± 4.84 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
------------------------------------------------------------------------
(?=x)(?!x)
210 ms ± 1.66 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
------------------------------------------------------------------------
x\by
7.41 ms ± 122 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
------------------------------------------------------------------------
x\bx
7.42 ms ± 110 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
------------------------------------------------------------------------
^\b$
108 ms ± 1.05 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
------------------------------------------------------------------------
\B\b
387 ms ± 5.77 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
------------------------------------------------------------------------
\ZNEVERMATCH\A
112 ms ± 1.52 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
------------------------------------------------------------------------
\Z\A
112 ms ± 1.38 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

我第一次运行此操作时,忘记了r最后3个表达式,因此'\b'被解释为'\x08',即退格符。但令我惊讶的是,'a\x08c'比之前的最快结果更快!公平地说,它仍然会匹配该文本,但我认为它仍然值得注意,因为我不确定为什么它会更快。

In [6]: for regex in ('x\by','x\bx','^\b$','\B\b'):
    ...:     print('-'*72)
    ...:     print(regex, repr(regex))
    ...:     %timeit re.search(regex,longfile)
    ...:     print(re.search(regex,longfile))
    ...:     
------------------------------------------------------------------------
y 'x\x08y'
5.32 ms ± 46.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
None
------------------------------------------------------------------------
x 'x\x08x'
5.34 ms ± 66.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
None
------------------------------------------------------------------------
$ '^\x08$'
122 ms ± 1.05 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
None
------------------------------------------------------------------------
\ '\\B\x08'
300 ms ± 4.11 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
None

我的测试文件是使用" ...Readable Contents And No Duplicate Lines"的公式创建的(在Ubuntu 16.04上):

$ ruby -e 'a=STDIN.readlines;275000.times do;b=[];rand(20).times do; b << a[rand(a.size)].chomp end; puts b.join(" "); end' < /usr/share/dict/words > /tmp/longfile.txt

$ head -n5 /tmp/longfile.txt 
unavailable speedometer's garbling Zambia subcontracted fullbacks Belmont mantra's
pizzicatos carotids bitch Hernandez renovate leopard Knuth coarsen
Ramada flu occupies drippings peaces siroccos Bartók upside twiggier configurable perpetuates tapering pint paralyzed
vibraphone stoppered weirdest dispute clergy's getup perusal fork
nighties resurgence chafe

答案 18 :(得分:1)

所有涉及边界匹配器的示例都遵循相同的配方。 食谱:

  1. 采用任何边界匹配器:^,$,\ b,\ A,\ Z,\ z

  2. 与它们的意图相反

示例:

^和\ A是开头,所以不要在开头使用它们

^ --> .^
\A --> .\A

\ b与单词边界匹配,因此请在两者之间使用

\b --> .\b.

$,\ Z和\ z用于结尾,所以不要在最后使用它们

$ --> $.
\Z --> \Z.
\z --> \z.

其他涉及先行和事后查找的用法,它们也可以使用相同的类比进行工作: 如果您给出正面或负面的前瞻,然后相反的话

(?=x)[^x]
(?!x)x

如果在相反的事物后面进行正向或负向回望

[^x](?<=x)
x(?<!x)

他们可能更多地是这种模式和更多的类比。

答案 19 :(得分:1)

也许这个?

/$.+^/

答案 20 :(得分:1)

(*FAIL)

(*F)

使用PCRE和PERL,您可以使用此回溯控制动词来强制模式立即失败。

答案 21 :(得分:0)

空正则表达式

一个绝不匹配任何东西的最佳正则表达式是一个空正则表达式。但是我不确定所有的正则表达式引擎都会接受。

不可能的正则表达式

另一种解决方案是创建一个不可能的正则表达式。我发现$-^仅需两个步骤即可计算,而与文本(https://regex101.com/r/yjcs1Z/1)的大小无关。

供参考:

  • $^$.采取了36个步骤来计算-> O(1)
  • \b\B在我的示例中执行了1507个步骤,并随着字符串中字符数的增加-> O(n)

有关此问题的更热门话题:

答案 22 :(得分:0)

\A[^\w\W]

不管正则表达式标志如何都有效。

根据regex101:对于空输入字符串,0步。对于所有其他输入字符串,只需 2 个步骤。

Kotlin 游乐场:https://pl.kotl.in/hdbNH73It

答案 23 :(得分:0)

如果不使用正则表达式,只需使用始终为false的if语句?在javascript中:

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

答案 24 :(得分:0)

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

并用所有可打印的符号替换...;)。这是一个文本文件。

答案 25 :(得分:-1)

不依赖于regexp实现的可移植解决方案就是使用常量 您确定永远不会出现在日志消息中的字符串。例如,根据以下内容创建一个字符串:

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

当然,这不是一个智力挑战,而是更像duct tape programming

答案 26 :(得分:-5)

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

创建一个仅包含字母数字和'-'的模式(其中没有一个是正则表达式特殊字符),但统计上不可能在同一个字符串之前出现过任何地方(因为这是GUID的整个点)。 )