def findall(pattern, string, flags=0):
"""Return a list of all non-overlapping matches in the string.
If one or more groups are present in the pattern, return a
list of groups; this will be a list of tuples if the pattern
has more than one group.
Empty matches are included in the result."""
return _compile(pattern, flags).findall(string)
The essence of an NFA engine is this: it considers each subexpression or component in turn, and whenever it needs to decide between two equally viable options,
it selects one and remembers the other to return to later if need be.
Situations where it has to decide among courses of action include anything with a
quantifier (decide whether to try another match), and alternation (decide which
alter native to try, and which to leave for later).
Whichever course of action is attempted, if it’s successful and the rest of the regex
is also successful, the match is finished. If anything in the rest of the regex eventually causes failure, the regex engine knows it can backtrack to where it chose the
first option, and can continue with the match by trying the other option. This way,
it eventually tries all possible permutations of the regex (or at least as many as
needed until a match is found).
为了避免灾难性的回溯,我建议
首先,我必须说它不是一个BUG。正因为如此,它尝试了所有的可能性,它需要时间和计算资源。有时它会吞噬很多时间。当它变得非常糟糕时,它被称为灾难性回溯。在
这是python source代码中
findall
函数的代码:如您所见,它只使用compile()函数,因此基于实际上使用
Traditional NFA
的_compile()
函数,python将其用于正则表达式匹配,并基于 这篇关于正则表达式回溯的简短解释,在掌握正则表达式,第三版,由Jeffrey E.F.Friedl!在让我们进入您的模式:这样您就有了
r'(\d+(,)?)+/$'
和这个字符串'12345121,223456,123123,3234,4523,523523'
我们有以下步骤:12345121
)与\d+
匹配,然后{(\d+(,)?)+
)之后的+
,整个字符串是匹配的/$
没有匹配的内容。因此,(\d+(,)?)+
需要“回溯”到最后一个字符之前,以便检查/$
。同样,它找不到任何合适的匹配,因此在这之后(,
)轮到回溯,然后\d+
将回溯,并且此回溯将继续结束,直到返回None
。 因此,根据字符串的长度,这需要时间,在本例中,时间非常长,并且它会创建一个完全嵌套的限定符!在作为一个近似的基准,在本例中,您有39字符,因此您需要3^39次回溯尝试(我们有3回溯方法)。在
为了更好地理解,我在改变字符串长度的同时测量程序的运行时间:
^{pr2}$为了避免这个问题,您可以使用以下方法之一:
{a1}的长度取决于非指数处理的长度。这与嵌套的重复和可选的逗号有关(即使某些正则表达式引擎可以确定这与尝试所有无关的重复不匹配)。这可以通过优化表达式来解决。在
实现这一点的最简单方法是只查找1+位数字或逗号,后跟斜杠和字符串的结尾:^{} 。但是,这并不是完美的,因为它允许
,123,,4,5/
之类的东西。在为此,您可以使用初始尝试的稍微优化的版本:^{} 。首先,我让你的重复组non-capturing(
(?:...)
),这是不必要的,但它提供了一个“更干净的匹配”。接下来,也是唯一关键的一步,我停止了在组内重复\d
,因为小组已经在重复了。最后,我删除了可选,
周围不必要的组,因为?
只影响最后一个字符。几乎这将寻找一个数字,可能是一个逗号,然后重复,最后是一个尾随/
。在这仍然可以匹配一个奇怪的字符串} 改进了原始正则表达式。这将断言在尾随的
1,2,3,/
,因此为了更好地使用negative lookbehind:^{/
之前没有逗号。在相关问题 更多 >
编程相关推荐