正则表达式模式灾难性回溯

Posted

技术标签:

【中文标题】正则表达式模式灾难性回溯【英文标题】:Regex Pattern Catastrophic backtracking 【发布时间】:2015-04-20 14:50:41 【问题描述】:

我在我的一个旧 Java 系统中使用了下面显示的正则表达式,这导致最近出现回溯问题。 回溯线程通常会导致机器的 CPU 达到上限,并且在应用程序重新启动之前它不会返回。

任何人都可以建议一种更好的方法来重写这种模式或可以帮助我这样做的工具吗?

图案:

^\[(([\pN]*\]\,\[[\pN]*)*|[\pN]*)\]$

价值观:

[1234567],[89023432],[124534543],[4564362],[1234543],[12234567],[124567],[1234567],[1234567]

灾难性的回溯值——如果值有任何问题(最后添加一个额外的大括号):

[1234567],[89023432],[124534543],[4564362],[1234543],[12234567],[124567],[1234567],[1234567]]

【问题讨论】:

你的意思是regex101.com/r/oV8pN3/1? 【参考方案1】:

当您的意思是 + 时,切勿使用 *。关于您的正则表达式,我注意到的第一件事是几乎所有内容都是可选的。只需要左方括号和右方括号,我很确定您不想将[] 视为有效输入。

失控回溯的最大原因之一是有两个或多个可以匹配相同事物的替代方案。这就是 |[\pN]* 部分所拥有的。正则表达式引擎必须在放弃之前尝试通过字符串的所有可能路径,因此所有这些 \pN* 构造都会在每一组数字上陷入无休止的拉锯战。

但是尝试解决这些问题是没有意义的,因为整体结构是错误的。我想这就是你要找的:

^\[\pN+\](?:,\[\pN+\])*$

在它使用第一个令牌([1234567])后,如果字符串中的下一个东西不是逗号或字符串的结尾,它会立即失败。如果它确实看到一个逗号,它必须继续匹配另一个完整的令牌 ([89023432]),否则它会立即失败。

这可能是您在创建正则表达式时要记住的最重要的事情:如果它会失败,您希望它尽快失败。为此,您可以使用原子组和所有格量词等功能,但如果您正确掌握了正则表达式的结构,则很少需要它们。回溯并非不可避免。

【讨论】:

感谢您的解释。它有助于解决问题。

以上是关于正则表达式模式灾难性回溯的主要内容,如果未能解决你的问题,请参考以下文章

这个正则表达式不应该发生灾难性的回溯

正则表达式灾难性回溯

如何彻底避免正则表达式的灾难性回溯?

如何使这个正则表达式不会导致“灾难性回溯”?

Go 正则表达式中没有灾难性的回溯吗?

Java 9+中的灾难性回溯正则表达式示例[关闭]