Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/regex/19.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python中的正则表达式出人意料地慢_Python_Regex_Python 3.x - Fatal编程技术网

Python中的正则表达式出人意料地慢

Python中的正则表达式出人意料地慢,python,regex,python-3.x,Python,Regex,Python 3.x,考虑以下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

考虑以下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
必须检查整个字符串几次才能找到正确的子字符串


但是在第二个例子中发生了什么?在子字符串不存在的情况下,正则表达式的速度惊人地慢!这让我感到惊讶,因为理论上正则表达式只需遍历字符串一次,而
任何
表达式都必须遍历字符串三次。这里怎么了?我的正则表达式有问题吗?或者Python正则表达式在这种情况下很慢吗?

正则表达式之所以如此慢,是因为它不仅要遍历整个字符串,而且每个字符都要进行多次计算

第一种方法就是这样做的:

f与h匹配吗?不
b和h匹配吗?不
h和h匹配吗?对
e和e匹配吗?对
我和我匹配吗?对
我和我匹配吗?对
o和o匹配吗?对
完成。找到匹配项。
第二个是这样的:

f与g匹配吗?不
b和g匹配吗?不
h和g匹配吗?不
f和o匹配吗?不
b和o匹配吗?不
h和o匹配吗?不
f和o匹配吗?不
b和o匹配吗?不
h和o匹配吗?不
f和d匹配吗?不
b和d匹配吗?不
h和d匹配吗?不
f和b匹配吗?不
b和b匹配吗?对
一根火柴会烧吗?不
h和b匹配吗?不
f和y匹配吗?不
b和y匹配吗?不
h和y匹配吗?不
f和e匹配吗?不
b和e匹配吗?不
h和e匹配吗?不
... 999次以上。。。
完成。没有找到匹配项。
我只能推测
any
和regex之间的区别,但我猜regex速度较慢,主要是因为它运行在一个高度复杂的引擎中,并且使用状态机之类的东西,它的效率不如具体实现(中的

在第一个字符串中,正则表达式几乎会在瞬间找到匹配项,而
any
必须在字符串中循环两次才能找到任何匹配项

但是,在第二个字符串中,
any
执行与正则表达式基本相同的步骤,但顺序不同。这似乎表明
any
解决方案更快,可能是因为它更简单

特定代码比一般代码更有效。有关问题的任何知识都可以用于优化解决方案。简单代码优于复杂代码。基本上,当模式接近字符串开头时,正则表达式速度更快,但当模式接近字符串结尾或根本找不到时,
中的
速度更快


免责声明:我不懂Python。我知道算法。

您有一个由三个regexp组成的regexp。如果regexp没有检查这三次,您认为这到底是如何工作的?:-)计算机没有魔力,你仍然需要做三次检查

但是regexp将逐个字符执行每三个测试,而“one()”方法将在进行下一个测试之前检查整个字符串是否匹配

在第一种情况下,regexp要快得多,这是因为您要检查最后匹配的字符串。这意味着
one()
需要首先在整个字符串中查找“foo”,然后查找“bar”,然后查找匹配的“hello”。首先移动“hello”,1()和2()的速度几乎相同,因为两种情况下的第一次匹配都成功

regexp是比“in”复杂得多的测试,所以我希望它会更慢。我怀疑当您使用“|”时,这种复杂性会增加很多,但我还没有阅读regexp库的源代码,所以我知道些什么。:-)

未来读者须知 我认为正确的答案实际上是Python的字符串处理算法针对这种情况进行了优化,
re
模块实际上要慢一点。我在下面写的是正确的,但可能与我在问题中使用的简单正则表达式无关

原始答案 显然这不是一个随机的侥幸-Python的
re
模块真的比较慢。它看起来像是在找不到匹配项时使用递归回溯方法,而不是构建DFA并模拟它

它使用回溯方法,即使正则表达式中没有回溯引用

这意味着在最坏的情况下,Python正则表达式需要指数时间,而不是线性时间

这是一篇非常详细的文章,描述了这个问题:

我认为这张接近结尾的图表简洁地概括了这一点:
我的同事找到了re2库()?有一个python包装器。在某些系统上安装有点困难


对于一些复杂的正则表达式和长字符串,我也遇到了同样的问题——re2大大加快了处理时间——从秒到毫秒。

至少我可以确认时间,但我不知道为什么+1表示奇怪和复制“n”可粘贴的示例。@JBernardo:您的意思是代替
one()
中的元组?事实上,这没什么区别。通过使用“
foo | bar | hello
”删除正则表达式中的组会使正则表达式使用3.5s而不是3.7s,但这仍然非常慢。如果使用更长的字符串(
'foo'*1000
)并排除简单的长度测试(将
hello*1000
更改为
hello*200
),您会发现正则表达式通常较慢,不仅仅是在第二个例子中。猜测一下,可能简单的字符串搜索是优化的,因此它会对每个汇编指令比较多个字节(使用一些SIMD指令?),而正则表达式不是。@仓鼠:
'foo'*1000
不是一个很有趣的例子,因为
any
表达式立即短路。而且正则表达式仍然很快