使用正则表达式模式时的灾难性回溯错误

Posted

技术标签:

【中文标题】使用正则表达式模式时的灾难性回溯错误【英文标题】:Catastrophic backtracking error when using a regex pattern 【发布时间】:2015-11-10 07:49:41 【问题描述】:

我的正则表达式模式是:

 (<span style="color:green">?(\s*|\w*|\S*)*<li>)(?! ?\s*<\/span>)

每当我尝试输入为:

 <span style="color:green"> anything <li> 

它工作正常,但每当发生最后一个条件时,即每当 li 标记后跟结束 span 标记时,它就会给出错误: 每当我的输入是:

<span style="color:green"> anything <li></span> 

我的模式中的实际问题是什么? 而不是: (?! ?\s*) 我也尝试过:

 ^(</span>)

但同样的错误发生了。我将把它嵌入到 php 中。

简而言之,我需要做的是:每当 li 开始标签位于 span 开始标签之后,但 span 结束标签不存在于 li 标签之后,那么我需要用一些东西替换我的 li 标签。

【问题讨论】:

正确答案是:使用 html 解析器而不是正则表达式。基于正则表达式的解决方法当然是可能的,但在解析任意 HTML 时,您永远不能依赖它们。 在 PHP 中,您可以轻松使用 DOMDocument 和 DOMXPath。请发布更多示例数据并指定您需要从中提取的内容。 @stribizhev 我的输入是: 标签和 li 结束标签之间的任何单词/标签/空格也应该在 span 开始和结束标签之间我想要的输出是 span 开始和结束标记都应该被删除,li 应该被我想要的东西替换 【参考方案1】:

由于(...|\w*|\S*)*&lt;,您的灾难性回溯发生。采取"an item"。在位置 0,正则表达式需要测试所有这些可能性

<
()<
(\w)<
(\w\w)<
(\w\w\w)
()<
(\S)<
(\S\S)<
(\S\S\S)
()()<
()(\w)<
()(\w\w)<
()(\w\w\w)
()()<
()(\S)<
()(\S\S)<
()(\S\S\S)
(\w)()<
(\w)(\w)<
(\w)(\w\w)
(\w)()<
(\w)(\S)<
(\w)(\S\S)
(\w\w)()<
(\w\w)(\w)
(\w\w)()<
(\w\w)(\S)
(\w\w\w)()
(\w\w\w)()
()()<
()(\w)<
()(\w\w)<
()(\w\w\w)
()()<
()(\S)<
()(\S\S)<
()(\S\S\S)
(\S)()<
(\S)(\w)<
(\S)(\w\w)
(\S)()<
(\S)(\S)<
(\S)(\S\S)
(\S\S)()<
(\S\)(\w)<
(\S\S)()<
(\S\S)(\S)
(\S\S\S)()
(\S\S\S)()
()()()<
()()(\w)<
()()(\w\w)<
()()(\w\w\w)
()()()<
()()(\S)<
()()(\S\S)<
()()(\S\S\S)
()(\w)()<
()(\w)(\w)<
()(\w)(\w\w)
()(\w)()<
()(\w)(\S)<
()(\w)(\S\S)
()(\w\w)()<
()(\w\w)(\w)
()(\w\w)()<
()(\w\w)(\S)
()(\w\w\w)()
()()()<
()()(\w)<
()()(\w\w)<
()()(\w\w\w)
()()()<
()()(\S)<
()()(\S\S)<
()()(\S\S\S)
()(\S)()<
()(\S)(\w)<
()(\S)(\w\w)
()(\S)()<
()(\S)(\S)<
()(\S)(\S\S)
()(\S\S)()<
()(\S\)(\w)<
()(\S\S)()<
()(\S\S)(\S)
()(\S\S\S)()
(\w)()()<
(\w)()(\w)<
(\w)()(\w\w)
(\w)()()<
(\w)()(\S)<
(\w)()(\S\S)
(\w)(\w)()<
(\w)(\w)(\w)
(\w)(\w)()<
(\w)(\w)(\S)
(\w)(\w\w)()
(\w)(\w\w)()
(\w)()()<
(\w)()(\w)<
(\w)()(\w\w)
(\w)()()<
(\w)()(\S)<
(\w)()(\S\S)
(\w)(\S)()<
(\w)(\S)(\w)
(\w)(\S)()<
(\w)(\S)(\S)
(\w)(\S\S)()
(\w)(\S)(\w)
(\w)(\S\S)()
(\w\w)()()<
(\w\w)()(\w)
(\w\w)()()<
(\w\w)()(\S)
(\w\w)(\w)()
(\w\w)(\w)()
(\w\w)()()<
(\w\w)()(\w)
(\w\w)()()<
(\w\w)()(\S)<
(\w\w)(\S)()
(\w\w)(\S)()
(\w\w\w)()()
(\S)()()<
(\S)()(\w)<
(\S)()(\w\w)
(\S)()()<
(\S)()(\S)<
(\S)()(\S\S)
(\S)(\w)()<
(\S)(\w)(\w)
(\S)(\w)()<
(\S)(\w)(\S)
(\S)(\w\w)()
(\S)(\w\w)()
(\S)()()<
(\S)()(\w)<
(\S)()(\w\w)
(\S)()()<
(\S)()(\S)<
(\S)()(\S\S)
(\S)(\S)()<
(\S)(\S)(\w)
(\S)(\S)()<
(\S)(\S)(\S)
(\S)(\S\S)()
(\S)(\S)(\w)
(\S)(\S\S)()
(\S\S)()()<
(\S\S)()(\w)
(\S\S)()()<
(\S\S)()(\S)
(\S\S)(\w)()
(\S\S)(\w)()
(\S\S)()()<
(\S\S)()(\w)
(\S\S)()()<
(\S\S)()(\S)
(\S\S)(\S)()
(\S\S)(\S)()
(\S\S\S)()()
...

为了消除"an " 作为可能的匹配项。 (我可能在某个地方搞砸了,但你明白了。)

还要注意\s|\S.(换行符除外)。但你真正想要的是“不是标签的开始”:[^&lt;]*&lt;/li&gt;。这样,为了消除 "an " 作为候选,正则表达式引擎只需要测试这些:

<
[^<]<
[^<][^<]<

(另外,对 stribizhev 的评论 +1:使用 HTML 处理库来处理 HTML。)

【讨论】:

任何其他标签都可以存在,但不仅仅是跨度结束标签,这就是我将它完全定义在那个括号内的原因。在这种情况下,任何起始标记都不起作用 我将正则表达式剥离到导致问题的位(非确定性交替\S|\w 和以下字符&lt; 使模式无效并触发回溯); li 不是问题的一部分。

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

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

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

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

正则表达式灾难性回溯

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

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