下面我有一个相当复杂的正则表达式似乎永远不会终止。这不仅仅需要花费很长时间 - 我已经等了好几分钟才得到回应而没有运气。
下面是一些可以重现问题的代码:
import re
link = re.compile(u'(?i)((?:https?://|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\((?:[^\s()<>]+|(?:\([^\s()<>]+\)))*\))+(?:\((?:[^\s()<>]+|(?:\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:\'".,<>?\xab\xbb\u201c\u201d\u2018\u2019]))', re.IGNORECASE)
text = "Check out this link http://ru.wikipedia.org/wiki/%D0%9B%D0%BE%D0%B2%D0%B5%D1%86_%D1%81%D0%BD%D0%BE%D0%B2_(%D0%B0%D0%BC%D1%83%D0%BB%D0%B5%D1%82"
matches = re.findall(link, text)
这里是将re.DEBUG作为标志传递给模式的输出:
subpattern 1
subpattern None
branch
literal 104
literal 116
literal 116
literal 112
max_repeat 0 1
literal 115
literal 58
literal 47
literal 47
or
literal 119
literal 119
literal 119
max_repeat 0 3
in
category category_digit
literal 46
or
max_repeat 1 4294967295
in
range (97, 122)
range (48, 57)
literal 46
literal 45
literal 46
max_repeat 2 4
in
range (97, 122)
literal 47
max_repeat 1 4294967295
subpattern None
branch
max_repeat 1 4294967295
in
negate None
category category_space
literal 40
literal 41
literal 60
literal 62
or
literal 40
max_repeat 0 4294967295
subpattern None
branch
max_repeat 1 4294967295
in
negate None
category category_space
literal 40
literal 41
literal 60
literal 62
or
subpattern None
literal 40
max_repeat 1 4294967295
in
negate None
category category_space
literal 40
literal 41
literal 60
literal 62
literal 41
literal 41
subpattern None
branch
literal 40
max_repeat 0 4294967295
subpattern None
branch
max_repeat 1 4294967295
in
negate None
category category_space
literal 40
literal 41
literal 60
literal 62
or
subpattern None
literal 40
max_repeat 1 4294967295
in
negate None
category category_space
literal 40
literal 41
literal 60
literal 62
literal 41
literal 41
or
in
negate None
category category_space
literal 96
literal 33
literal 40
literal 41
literal 91
literal 93
literal 123
literal 125
literal 59
literal 58
literal 39
literal 34
literal 46
literal 44
literal 60
literal 62
literal 63
literal 171
literal 187
literal 8220
literal 8221
literal 8216
literal 8217
答案 0 :(得分:3)
如果你从字符串末尾删除%D0%B5%D1%82
,它就会起作用(尽管大约是10秒)。随着您增加字符串和匹配的复杂性,您将指数增加检查表达式条件所需的处理量,并且您可能会将CPU一直加速到100%。
这被称为catastrphic backtracking - 它让我觉得你有99到100个问题;)
CPU被阻止到无法继续处理表达式的程度。
<强>解决方案:强>
老实说,我不知道这是否会为你削减它,但由于你有很多重叠的条件,这似乎会削减它们。最好检查一堆样本来验证:
(?i)(?:https?://|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)((?:\(?[^\s()<>]+\)?)*[^\s`!()\[\]{};:\'".,<>?\xab\xbb\u201c\u201d\u2018\u2019])
示例:http://regex101.com/r/lV0oL4
我相信你可以进一步简化它,这只是一个快速的尝试。字符串的处理时间:小于100毫秒。
答案 1 :(得分:1)
这可能无济于事。如果Python支持积极的量词,我不知道(但不要这么认为) 但如果确实如此,下面就会使其免于灾难性的回溯 我试图制作一个相当于停止回溯而没有积极的东西 确实如此,但意义发生了变化,并没有达到相同的效果。
你的正则表达式是每个细节的模拟递归正则表达式。您可以在下面看到重复的模式 这是这里的主要问题,因为它必须具有积极性,因为在现实中,它匹配可选的平衡文本,并且没有限制阻止它回溯。
在这种情况下,我认为没有办法获得一个等效(非回溯)正则表达式。
因此,如果它不能做有量的量词,那么正则表达式在其使用的任何上下文中都注定要失败。
# (?i)((?:https?://|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\((?:[^\s()<>]+|(?:\([^\s()<>]+\)))*+\))+(?:\((?:[^\s()<>]+|(?:\([^\s()<>]+\)))*+\)|[^\s`!()\[\]{};:\'".,<>?\xab\xbb\u201c\u201d\u2018\u2019]))
# matches: 'http://ru.wikipedia.org/wiki/%D0%9B%D0%BE%D0%B2%D0%B5%D1%86_%D1%81%D0%BD%D0%BE%D0%B2_'
(?i)
(
(?:
https?://
| www \d{0,3} [.]
| [a-z0-9.\-]+ [.] [a-z]{2,4} /
)
(?:
[^\s()<>]+
| \(
(?:
[^\s()<>]+
| (?: \( [^\s()<>]+ \) )
)*+ # <- Possesive
\)
)+
(?:
\(
(?:
[^\s()<>]+
| (?: \( [^\s()<>]+ \) )
)*+ # <- Possesive
\)
| [^\s`!()\[\]{};:\'".,<>?\xab\xbb\u201c\u201d\u2018\u2019]
)
)