Go 正则表达式中没有灾难性的回溯吗?
Is there no catastrophic backtracking in Go regex?
今天在 regex101.com 上测试这个正则表达式时,
^([a-z0-9]+(-)*)*([a-z0-9])$
我在这个字符串上测试它时遇到“灾难性回溯”错误:
有味道PHP:
aaaaaaaaaaa-aaaaT
有味道Python:
aaaaaaaaaaa-aaaaT
具有 ECMAScript 风格的这个较长的字符串超时 'may be an indication of catastrophic backtracking'
aaaaaaaaaaa-aaaaaaaaaaaaaaaaaT
with flavor Java 8 timeout with string
aaaaaaaaaaa-aaaaaaaaaaaaaT
但是 flavor Go 对于更长的字符串没有给出错误或超时事件。相反,它显示 no match (0.0ms)
当我的正则表达式在 Go 中使用时,我可以忽略 error/warning 吗?
我也很想知道其中的原因,但以上是我的关键问题。
是的,在 Go 中使用正则表达式时可以安全地忽略灾难性回溯警告。
Go使用regex的RE2算法,RE2没有使用回溯,所以Go不会出现这个问题。
https://en.wikipedia.org/wiki/Regular_expression#Implementations_and_running_times 有更多关于正则表达式匹配的替代实现的信息。
Go (RE2) 对输入字符串长度和正则表达式字符串长度具有线性性能:O(mn).
然而,其他使用回溯的语言/库可能有指数 运行ning 时间,这取决于正则表达式和输入字符串。
regex101.com 显示针对输入字符串 运行 正则表达式的步数,您可以看到随着正则表达式的字符串长度的增加,步数呈指数增长,例如 (a*)*$
喜欢 aaaaaaaaaaaaaaaaX
。 regex101.com 上的调试器可以一次显示模式匹配执行的一个步骤,因此您可以看到回溯必须如何处理呈指数增长的备选方案。
@sln 提供了我原来的正则表达式的替代方案,它删除了指数回溯。
将 before/after 正则表达式简化为 a
和 X
,对于输入字符串 aaaaaaaaaaaaaaaaZ
^(a+X*)*a$
大约需要 300,000 步(每增加一倍 a
)但是
^(aX*)*a$
大约需要 100 步
我不知道将易受攻击的正则表达式映射到安全正则表达式的任何通用方法 - 除非@sln 关心提供服务;-)
原始正则表达式的目的是检查输入字符串是否仅包含 [a-z0-9] 和 -
,同时以 [a-z0-9] 开头和结尾。
a
、a-b
、ab--c
、a-b--aa---bbb
、...
今天在 regex101.com 上测试这个正则表达式时,
^([a-z0-9]+(-)*)*([a-z0-9])$
我在这个字符串上测试它时遇到“灾难性回溯”错误:
有味道PHP:
aaaaaaaaaaa-aaaaT
有味道Python:
aaaaaaaaaaa-aaaaT
具有 ECMAScript 风格的这个较长的字符串超时 'may be an indication of catastrophic backtracking'
aaaaaaaaaaa-aaaaaaaaaaaaaaaaaT
with flavor Java 8 timeout with string
aaaaaaaaaaa-aaaaaaaaaaaaaT
但是 flavor Go 对于更长的字符串没有给出错误或超时事件。相反,它显示 no match (0.0ms)
当我的正则表达式在 Go 中使用时,我可以忽略 error/warning 吗?
我也很想知道其中的原因,但以上是我的关键问题。
是的,在 Go 中使用正则表达式时可以安全地忽略灾难性回溯警告。
Go使用regex的RE2算法,RE2没有使用回溯,所以Go不会出现这个问题。 https://en.wikipedia.org/wiki/Regular_expression#Implementations_and_running_times 有更多关于正则表达式匹配的替代实现的信息。 Go (RE2) 对输入字符串长度和正则表达式字符串长度具有线性性能:O(mn).
然而,其他使用回溯的语言/库可能有指数 运行ning 时间,这取决于正则表达式和输入字符串。
regex101.com 显示针对输入字符串 运行 正则表达式的步数,您可以看到随着正则表达式的字符串长度的增加,步数呈指数增长,例如 (a*)*$
喜欢 aaaaaaaaaaaaaaaaX
。 regex101.com 上的调试器可以一次显示模式匹配执行的一个步骤,因此您可以看到回溯必须如何处理呈指数增长的备选方案。
@sln 提供了我原来的正则表达式的替代方案,它删除了指数回溯。
将 before/after 正则表达式简化为 a
和 X
,对于输入字符串 aaaaaaaaaaaaaaaaZ
^(a+X*)*a$
大约需要 300,000 步(每增加一倍 a
)但是
^(aX*)*a$
大约需要 100 步
我不知道将易受攻击的正则表达式映射到安全正则表达式的任何通用方法 - 除非@sln 关心提供服务;-)
原始正则表达式的目的是检查输入字符串是否仅包含 [a-z0-9] 和 -
,同时以 [a-z0-9] 开头和结尾。
a
、a-b
、ab--c
、a-b--aa---bbb
、...