经过长时间的调试,我发现了为什么我的使用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 et Hippolyte(.*)
导致的。搜索(.*)sol
的时间复杂度完全相同。实际上,如果字符串包含sol
,并且您使用findall()
进行搜索,那么(.*)sol
实际上会更糟糕,因为它会在跟随sol
的子字符串上触发一个失败的回溯搜索(原始正则表达式将在成功时消耗整个字符串)。 - alexisregex
包 - 运行得很好!对于相同的字符串立即得到了结果。从这里下载:https://pypi.python.org/pypi/regex - SomethingSomething