从未完成运行的正则表达式

时间:2012-10-05 15:14:21

标签: python regex

我写了一个小的,天真的正则表达式,应该在括号内找到文本:

re.search(r'\((.|\s)*\)', name)

我知道这不是出于某些原因这样做的最佳方式,但它工作得很好。我正在寻找的只是一个解释,为什么对于某些字符串,这个表达式开始呈指数级增长然后永远不会完成。昨晚,在运行此代码数月之后,我们的一台服务器突然卡住了匹配类似于以下内容的字符串:

x (y)                                            z

我已经对它进行了实验,并确定在“y”和“z”之间的每个空间所花费的时间都会翻倍:

In [62]: %timeit re.search(r'\((.|\s)*\)', 'x (y)' + (22 * ' ') + 'z')
1 loops, best of 3: 1.23 s per loop

In [63]: %timeit re.search(r'\((.|\s)*\)', 'x (y)' + (23 * ' ') + 'z')
1 loops, best of 3: 2.46 s per loop

In [64]: %timeit re.search(r'\((.|\s)*\)', 'x (y)' + (24 * ' ') + 'z')
1 loops, best of 3: 4.91 s per loop

但是除了空格之外的其他字符也没有相同的效果:

In [65]: %timeit re.search(r'\((.|\s)*\)', 'x (y)' + (24 * 'a') + 'z')
100000 loops, best of 3: 5.23 us per loop

注意:我不是在寻找更好的正则表达式或此问题的其他解决方案。我们不再使用它了。

2 个答案:

答案 0 :(得分:6)

灾难性回溯

正如CaffGeek的答案正确暗示的那样,问题是由catastrophic backtracking的一种形式引起的。这两个选项都匹配一个空格(或制表符),这是无限次地贪婪地应用。此外,点匹配右括号,因此一旦打开paren匹配,此表达式始终匹配字符串的最末端,然后必须艰苦地回溯以找到结束括号。在此回溯过程中,在每个位置尝试另一种替代方案(对于空格或制表符也是成功的)。因此,必须在引擎可以回溯一个位置之前尝试每个可能的匹配组合序列。在收盘后有很多空间,这很快就会增加。通过简单地使星形量化器变得懒惰(即r'\((.|\s)*?\)'),可以解决存在匹配关闭paren的情况下的具体问题,但是对于存在开口的非匹配情况,仍然存在失控的正则表达式问题。 paren在主题字符串中没有匹配的关闭paren。

原始正则表达式确实是,真的糟糕! (如果有多对,也不能正确匹配关闭的parens。)

匹配最里面括号的正确表达式(对于匹配和非匹配情况都非常快),当然是:

re_innermostparens = re.compile(r"""
    \(        # Literal open paren.
    [^()]*    # Zero or more non-parens.
    \)        # Literal close paren.
    """, re.VERBOSE)

所有正则表达式作者都应阅读MRE3!

这一切都在Jeffrey Friedl的必须阅读的正则表达式作者Mastering Regular Expressions (3rd Edition)中进行了详细解释(详尽的例子和推荐的最佳实践)。我可以诚实地说,这是我读过的最有用的书。正则表达式是一个非常强大的工具,但是像装载的武器一样必须非常小心和精确地应用(或者你会用脚射击自己!)

答案 1 :(得分:5)

可能是ReDos问题。

请参阅:http://en.wikipedia.org/wiki/ReDoS

可以在构造不良的正则表达式上创建正则表达式拒绝服务。

例如,从这里开始:https://www.owasp.org/index.php/Regular_expression_Denial_of_Service_-_ReDoS

此正则表达式^(a+)+$

enter image description here

对于输入aaaaX,上图中有16条可能的路径。但是对于aaaaaaaaaaaaaaaaX,有65536条可能的路径,每增加一条路径,这个数字就是两倍。这是一个极端的情况,其中天真的算法是有问题的,因为它必须传递许多路径,然后失败。

我怀疑你的正则表达式的很大一部分问题是(.|\s)这有点令人困惑,因为\s已经包含.。但是创造了一个选择点。

编辑:我认为这是我读过的问题的一个更好的解释。

http://msdn.microsoft.com/en-us/magazine/ff646973.aspx