【发布时间】:2014-10-06 10:28:31
【问题描述】:
经过长时间的调试,我发现为什么我的应用程序使用 python 正则表达式很慢。以下是我感到惊讶的事情:
import datetime
import re
pattern = re.compile('(.*)sol(.*)')
lst = ["ciao mandi "*10000 + "sol " + "ciao mandi "*10000,
"ciao mandi "*1000 + "sal " + "ciao mandi "*1000]
for s in lst:
print "string len", len(s)
start = datetime.datetime.now()
re.findall(pattern,s)
print "time spent", datetime.datetime.now() - start
print
我机器上的输出是:
string len 220004
time spent 0:00:00.002844
string len 22004
time spent 0:00:05.339580
第一个测试字符串220K长,匹配,匹配速度相当快。第二个测试字符串20K长,不匹配,计算需要5秒!
我知道这份报告http://swtch.com/~rsc/regexp/regexp1.html 说python、perl、ruby 中的正则表达式实现在某种程度上不是最佳的……这是原因吗?没想到这么简单的表达方式会发生这种事。
添加 我最初的任务是拆分一个字符串,依次尝试不同的正则表达式。比如:
for regex in ['(.*)sol(.*)', '\emph{([^{}])*)}(.*)', .... ]:
lst = re.findall(regex, text)
if lst:
assert len(lst) == 1
assert len(lst[0]) == 2
return lst[0]
这是为了解释为什么我不能使用split。按照 Martijn 的建议,我通过将 (.*)sol(.*) 替换为 (.*?)sol(.*) 解决了我的问题。
可能我应该使用match 而不是findall... 但我认为这不会解决问题,因为正则表达式将匹配整个输入,因此 findall 应该在第一次匹配时停止。
无论如何,我的问题更多的是关于正则表达式新手遇到这个问题有多容易......我认为理解(.*?)sol(.*) 是解决方案并不是那么简单(例如(.*?)sol(.*?) 不是) .
【问题讨论】:
-
不,原因不是执行。原因是这两个
.*过于放纵,造成灾难性的回溯。你到底想做什么? -
@casimir,灾难性的回溯是的实现问题。阅读 Emanuele 链接的文章。
-
@alexis:不,这种情况下的灾难性回溯是由于模式概念造成的。使用其他 NFA 引擎,您将获得或多或少相同的结果。
-
阅读文章。 “其他 NFA 引擎”具有相同的实现。真正的 FSA 不需要任何回溯。
-
再注意:问题不是由双
(.*)引起的。搜索(.*)sol具有完全相同的时间配置文件。事实上,如果字符串包含sol并且您使用findall()搜索,(.*)sol实际上更糟糕,因为它会触发对sol后面的子字符串的回溯搜索失败。 (原始的 RE 将在成功时消耗整个字符串)。