考虑这个Python代码:
import timeit
import re
def one():
any(s in mystring for s in ('foo', 'bar', 'hello'))
r = re.compile('(foo|bar|hello)')
def two():
r.search(mystring)
mystring="hello"*1000
print([timeit.timeit(k, number=10000) for k in (one, two)])
mystring="goodbye"*1000
print([timeit.timeit(k, number=10000) for k in (one, two)])
基本上,我正在使用两种方法来检查大字符串中几个子串之一的存在。
我得到的是什么(Python 3.2.3)是这个输出:
[0.36678314208984375, 0.03450202941894531]
[0.6672089099884033, 3.7519450187683105]
在第一种情况下,正则表达式很容易使any
表达式失败 - 正则表达式立即找到子字符串,而any
必须在它到达之前检查整个字符串几次正确的子串。
但是第二个例子中发生了什么?在子字符串不存在的情况下,正则表达式非常慢!这让我感到惊讶,因为从理论上讲,正则表达式只需要遍历字符串一次,而any
表达式必须超过字符串3次。这有什么不对?我的正则表达式有问题,或者Python正则表达式在这种情况下是否会变慢?
答案 0 :(得分:25)
我认为正确的答案实际上是Python的字符串处理算法真的针对这种情况进行了优化,而re
模块实际上有点慢。我在下面写的是真的,但可能与我在问题中的简单正则表达式无关。
显然这不是一个随意的侥幸 - Python的re
模块确实比较慢。看起来它在找不到匹配时使用了递归回溯方法,而不是构建DFA并模拟它。
即使正则表达式中没有反向引用,它也会使用回溯方法!
这意味着在最坏的情况下,Python正则表达式采用指数而非线性的时间!
这是一篇非常详细的文章,描述了这个问题: http://swtch.com/~rsc/regexp/regexp1.html
我认为这张图靠近末尾简洁地概括了它:
答案 1 :(得分:3)
我的同事找到了re2库(https://code.google.com/p/re2/)?有一个python包装器。在某些系统上安装它有点小。
我遇到了一些复杂的正则表达式和长串的问题 - 显着加快了处理时间 - 从几秒到几毫秒。
答案 2 :(得分:2)
正则表达式如此之慢的原因是因为它不仅需要遍历整个字符串,而且还需要对每个字符进行多次计算。
第一个就是这样做:
Does f match h? No.
Does b match h? No.
Does h match h? Yes.
Does e match e? Yes.
Does l match l? Yes.
Does l match l? Yes.
Does o match o? Yes.
Done. Match found.
第二个做到了这一点:
Does f match g? No.
Does b match g? No.
Does h match g? No.
Does f match o? No.
Does b match o? No.
Does h match o? No.
Does f match o? No.
Does b match o? No.
Does h match o? No.
Does f match d? No.
Does b match d? No.
Does h match d? No.
Does f match b? No.
Does b match b? Yes.
Does a match y? No.
Does h match b? No.
Does f match y? No.
Does b match y? No.
Does h match y? No.
Does f match e? No.
Does b match e? No.
Does h match e? No.
... 999 more times ...
Done. No match found.
我只能推测any
和正则表达式之间的区别,但我猜测正则表达式较慢,主要是因为它运行在一个高度复杂的引擎中,而且状态机的东西和所有东西,它只是不是与特定实现(in
)一样高效。
在第一个字符串中,正则表达式几乎会立即找到匹配项,而any
必须在找到任何内容之前循环遍历字符串两次。
然而,在第二个字符串中,any
执行与正则表达式基本相同的步骤,但顺序不同。这似乎指出any
解决方案更快,可能是因为它更简单。
特定代码比通用代码更有效。任何有关该问题的知识都可用于优化解决方案。简单代码比复杂代码更受欢迎。本质上,当模式接近字符串的开头时,正则表达式更快,但当模式接近字符串末尾时,in
更快,或者根本找不到。
免责声明:我不懂Python。我知道算法。
答案 3 :(得分:1)
你有一个由三个正则表达式组成的正则表达式。如果正则表达式没有检查三次,那么您认为这是如何工作的? :-)计算中没有魔力,你仍然需要做三次检查。
但正则表达式将逐个字符地执行每个三个测试,而#34; one()"在进入下一个匹配之前,方法将检查整个字符串是否匹配。
在第一种情况下正则表达式要快得多是因为你检查了最后匹配的字符串。这意味着one()
需要首先查看" foo"的整个字符串,然后查看" bar"然后为"你好",它匹配的地方。移动"你好"首先,one()和two()几乎是相同的速度,因为在两种情况下完成的第一场比赛都成功。
正则表达式比#34;中的#34;更复杂。所以我希望它会变慢。我怀疑当你使用" |"时,这种复杂性会增加很多,但我还没有读取正则表达式库的来源,所以我知道什么。 : - )