繁体   English   中英

非常慢的正则表达式搜索

[英]Very slow regular expression search

我不确定我是否完全理解以下正则表达式搜索的内容:

>>> import re
>>> template = re.compile("(\w+)+\.")
>>> target = "a" * 30
>>> template.search(target)

search()调用需要几分钟才能完成,CPU使用率达到100%。 对于2.7.5和3.3.3 Python版本,该行为都是可重现的。

有趣的是,如果字符串的长度小于20-25个字符,则search()返回。

怎么了?

了解此问题需要了解NFA在RegExp下的工作方式。

阐述NFA的定义对我来说可能是一项过于繁重的任务。 在维基上搜索NFA,它会为您提供更好的解释。 这里只想NFA是一个机器人发现你给出的模式。

粗暴地实施NFA有点愚蠢,它只是向前看你给的一两个代币。 所以在你给出的合成例子中,NFA首先看起来是\\w+ (不是用于分组的括号)。

因为+是一个贪婪的量词,也就是说,匹配尽可能多的字符,所以NFA勉强继续消耗target字符。 30后a S,NFA遇到字符串的结束。 之后,NFA意识到需要在template匹配其他令牌。 下一个标记是+ NFA已匹配,因此它将进入\\. 这次失败了。

NFA接下来要做的就是将一个字符后退一步,尝试通过截断\\w+匹配来匹配模式。 所以NFA将target分成两组, a组是一个\\w+ ,另一组a NFA首先尝试通过将其与第二个+匹配来消耗尾随a,但是当NFA会议\\.时它仍然会失败\\. NFA继续上述过程,直到获得完全匹配,否则它将尝试所有可能的分区。

所以(\\w+)+\\. 指示NFA以这种方式对target进行分组:目标被划分为一个或多个组,每组至少一个字符,目标以句点'。'结束。 只要期间不匹配。 NFA尝试所有分区。 那么有多少分区? 2 ^ n,指数为2.(JUst认为在a之间插入分隔符)。 如下

aaaaaaa a
aaaaaa aa
aaaaaa a a
.....
.......
aa a a ... a
a a a a a .... a

如果NFA匹配\\. ,它不会伤害太多。 但是当它无法匹配时,这个表达式注定永无止境的指数。

我不是广告,但掌握正则表达式是理解RegExp机制的好书。

缓慢是由引擎的回溯引起的:

(\w+)+\.

如果没有,这种模式自然会发生回溯. 在你的字符串的末尾。 当引擎发现在字符串结束之前需要匹配更多字符时,引擎将首先尝试匹配尽可能多的\\w并回溯。

(a x 59) .
(a x 58) .
...
(a) .

最后它将无法匹配。 但是,模式中的第二个+会导致引擎检查(n-1)! 可能的路径,所以:

(a x 58) (a) .
(a x 57) (a) (a) .
(a x 57) (a x 2) .
...
(a) (a) (a) (a) (a) (a) (a) ...

删除+将防止异常的回溯量:

(\w+)\.

一些实现还将支持占有量词,在这种特定场景中可能更理想:

(\w++)\.

第二个加号导致问题:

template = re.compile("(\w+)\.")

对我来说很好。 要查看正则表达式的解析树,请将re.DEBUG作为第二个参数传递给:

import re

re.compile("(\w+)+\.", re.DEBUG)
print "\n\n"
re.compile("(\w+)\.", re.DEBUG)


max_repeat 1 65535
  subpattern 1
    max_repeat 1 65535
      in
        category category_word
literal 46


subpattern 1
  max_repeat 1 65535
    in
      category category_word
literal 46

进程以退出代码0结束

这证明了第二个加号是添加一个循环,python正则表达式解析器必须限制在65535.这有点证明了我的理论。

请注意,要运行它,您将需要为每次执行提供一个新的python解释器。 re.compile memoizes传入的值,因此它不会重新编译相同的正则表达式两次,例如在ipython中重复运行它不会在第一次运行它时打印出解析树。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM