了解是什么让这个正则表达式变得如此缓慢

时间:2016-03-27 13:57:05

标签: python regex regex-greedy

我有一个正则表达式:

import re

regexp = re.compile(r'^(?P<parts>(?:[\w-]+/?)+)/$')

它匹配foo/bar/baz/之类的字符串,并将foo/bar/baz放在名为parts的组中(/?/$支持此组合)。< / p>

这完全没问题,直到你匹配一个不以斜线结尾的字符串。然后,它会以看似指数的速度变慢,每个新的字符都会添加到您匹配的字符串中。

实施例

# This is instant (trailing slash)
regexp.match('this-will-take-no-time-at-all/')

# This is slow
regexp.match('this-takes-about-5-seconds')

# This will not finish
regexp.match('this-probably-will-not-finish-until-the-day-star-turns-black')

我试图理解为什么只有当/$(尾部斜杠)不在字符串中时(即不匹配)才会发生这种特定的递归问题。你能帮我理解尾部斜杠和非尾部斜线情况下底层算法的控制流程吗?

注意

我没有为我想要的模式寻找解决方案。我试图了解具体的正则表达式。

2 个答案:

答案 0 :(得分:3)

由于你的正则表达式中的catastrophic backtracking,它变慢了:

您可以使用此正则表达式修复灾难性的回溯:

^(?P<parts>(?:[\w-]+/)*[\w-]+)/$

根据上面的链接:

  

避免灾难性回溯解决方案很简单。嵌套重复运算符时,请确保只有一种方法可以匹配相同的匹配。

答案 1 :(得分:1)

Wiktor Stribizew是正确的。问题是重复模式中斜线后面的问号。那么,你给出的模式:

'^(?P<parts>(?:[\w-]+/?)+)/$'

说找一个或多个一个或多个单词字符或短划线的组可能后面跟一个斜线,然后最后应该有一个斜线。

因此,对于像arm/这样的字符串,内部分组可以是以下任何一种:

(arm)/
(ar)(m)/
(a)(rm)/
(a)(r)(m)/

随着字符串变长,如果正则表达式在第一次尝试时无法匹配,它将检查越来越多的组合以尝试匹配。为避免这种情况,可以通过以下方式实现相同的匹配:

'^(?P<parts>(?:[\w-]+/)*[\w-]+)/$'

因为此版本只能以一种方式匹配您的目标字符串。