与任何字符串不匹配的执行速度最快的正则表达式是什么?这似乎是一件无用的事情,但考虑一个程序,它将强制正则表达式作为例子过滤器(这实际上是我的场景)。我尝试了一些,发现b(?<!b)
是最佳表现者,因为b
在输入中很少出现。
这是我编写的一个python代码,用于测试速度的不同模式:
#!/usr/bin/env python
import re
import time
tests = [
r'a\A',
r'b\A',
r'a^',
r'b^',
r'[^\s\S]',
r'^(?<=a)',
r'^(?<=b)',
r'a(?<!a)',
r'b(?<!b)',
r'\Za',
r'\Zb',
r'$a',
r'$b'
]
timing = []
text = 'a' * 50000000
for t in tests:
pat = re.compile(t)
start = time.time()
pat.search(text)
dur = time.time() - start
timing.append((t, dur))
timing.sort(key=lambda x: x[1])
print('%-30s %s' % ('Pattern', 'Time'))
for t, dur in timing:
print('%-30s %0.3f' % (t, dur))
在我的机器上,我得到以下时间:
Pattern Time
b(?<!b) 0.043
b\A 0.043
b^ 0.043
$a 0.382
$b 0.382
^(?<=a) 0.395
\Za 0.395
\Zb 0.395
^(?<=b) 0.414
a\A 0.437
a^ 0.440
a(?<!a) 0.796
[^\s\S] 1.469
更新:为一些建议的正则表达式添加了基准。
答案 0 :(得分:2)
单个字符是有效的正则表达式。一个不是“魔法”的单个角色会与自己匹配。如果你能识别出一个永远不会出现在你的文本中的单个字符,你可以从中创建一个模式。
ASCII NUL,字符0怎么样?
我在测试程序中又插入了一个字符串,字符串:'\0'
它与您最好的模式一样快:b(?<!b)
好的,你在字符串结尾后已经有了一个字符。在字符串开头之前字符怎么样?这是不可能的:'x^'
啊哈!这比在字符串结束后检查字符要快。但它与你最好的模式一样快。
我建议用ASCII NUL替换b
并将其称为好。当我尝试这种模式时:\0(?<!\0)
它赢了一小部分。但实际上,在我的计算机上,上面讨论的所有内容都非常接近,以至于没有太多可以区分它们。
结果:
Pattern Time
\0(?<!\0) 0.098
\0 0.099
x^ 0.099
b(?<!b) 0.099
^(?<=x) 1.416
$b 1.446
$a 1.447
\Za 1.462
\Zb 1.465
[^\s\S] 2.280
a(?<!a) 2.843
那是有趣。感谢您发布问题。
编辑:啊哈哈!我重写了程序,用真实的输入数据进行测试,得到了不同的结果。我从Project Gutenberg下载了“William Shakespeare全集”作为文本文件。 (很奇怪,它在wget
上出错,但让我的浏览器得到它...某种措施可以防止自动复制?)网址:http://www.gutenberg.org/cache/epub/100/pg100.txt
以下是结果,然后在我运行时修改了程序。
Pattern Time
\0(?<!\0) 0.110
\0 0.118
x^ 0.119
b(?<!b) 0.143
a(?<!a) 0.275
^(?<=x) 1.577
$b 1.605
$a 1.611
\Za 1.634
\Zb 1.634
[^\s\S] 2.441
所以是的,我肯定会选择第一个。
#!/usr/bin/env python
import re
import time
tests = [
r'x^',
r'\0',
r'[^\s\S]',
r'^(?<=x)',
r'a(?<!a)',
r'b(?<!b)',
r'\0(?<!\0)',
r'\Za',
r'\Zb',
r'$a',
r'$b'
]
timing = []
#text = 'a' * 50000000
text = open("/tmp/pg100.txt").read()
text = text * 10
for t in tests:
pat = re.compile(t)
start = time.time()
pat.search(text)
dur = time.time() - start
timing.append((t, dur))
timing.sort(key=lambda x: x[1])
print('%-30s %s' % ('Pattern', 'Time'))
for t, dur in timing:
print('%-30s %0.3f' % (t, dur))
答案 1 :(得分:0)
这可能不是你正在寻找的那种答案,但我在这里摆弄了几分钟,我能做的最好就是:'a{%d}' % (len(input)+1)
。或者,换句话说,将您的模式计算为最大量词长度超过字符串的已知长度的任何字符。这显然只有在您有权访问输入字符串时才有效。它似乎也很好地扩展;我将文本变量输出到texts = [os.urandom(20000) for x in range(20000)]
,在这种情况下,a{20001}
仍然在0.013
。
更新了脚本:
import os
import re
import time
tests = [
r'\0(?<!\0)',
r'\0',
r'x^',
r'a{2001}',
r'a(?<!a)',
r'b(?<!b)',
r'\Za',
r'\Zb',
r'$a',
r'$b'
]
texts = [os.urandom(2000) for x in range(20000)]
flag = 0
for t in tests:
pat = re.compile(t)
start = time.time()
for text in texts:
if pat.search(text):
print('%-30s %10s' % (t, "FAILED"))
flag = 1
break
if flag == 0:
dur = time.time() - start
print('%-30s %0.3f' % (t, dur))
else:
flag = 0
基准:
\0(?<!\0) 0.058
\0 FAILED
x^ 0.055
a{2001} 0.022
a(?<!a) 0.060
b(?<!b) 0.073
\Za 0.798
\Zb 0.797
$a 0.757
$b 0.767
答案 2 :(得分:0)
通常我们会使用直接失败的简短(?!)
。
如果你想要更明确Perl / PCRE有你可以使用的(*FAIL)
或(*F)
动词。