【问题标题】:Java regex to match start/end tags causes stack overflowJava 正则表达式匹配开始/结束标签会导致堆栈溢出
【发布时间】:2016-12-17 10:19:53
【问题描述】:

JavaPattern 类的标准实现使用递归来实现多种形式的正则表达式(例如,某些运算符、交替)。

这种方法会导致超过(相对较小)长度的输入字符串出现堆栈溢出问题,长度可能不超过 1,000 个字符,具体取决于所涉及的正则表达式。

这方面的一个典型示例是以下正则表达式,它使用交替从已经提供的周围 XML 字符串中提取可能的多行元素(名为 Data):

<Data>(?<data>(?:.|\r|\n)+?)</Data>

上面的正则表达式与Matcher.find()方法一起使用来读取“数据”捕获组并按预期工作,直到提供的输入字符串的长度超过1200个字符左右,在这种情况下会导致堆栈溢出.

可以重写上面的正则表达式来避免堆栈溢出问题吗?

【问题讨论】:

  • 你不应该使用正则表达式来解析像 XML 这样的问题。 XML 解析器并不难找到。
  • 当然!我们确实使用了 XML 解析器,但在此之前我们正在进行一些文本预处理,这比上面的示例更复杂。 :-)

标签: java regex pattern-matching stack-overflow matcher


【解决方案1】:

关于origin of the stack overflow issue的更多细节:

有时正则表达式Pattern 类会抛出StackOverflowError。这是known bug #5050507 的一种表现形式,自Java 1.4 以来一直在java.util.regex 包中。该错误会一直存在,因为它具有“无法修复”状态。发生此错误是因为Pattern 类将正则表达式编译成一个小程序,然后执行该小程序以查找匹配项。该程序以递归方式使用,有时当进行太多递归调用时会发生此错误。有关详细信息,请参阅description of the bug它似乎主要是由使用交替触发的。

您的正则表达式(有交替)匹配两个标签之间的任何 1+ 个字符。

您可以使用带有 Pattern.DOTALL 修饰符(或等效的嵌入式标志 (?s))的惰性点匹配模式,这将使 . 也匹配换行符:

(?s)<Data>(?<data>.+?)</Data>

this regex demo

但是,在输入量很大的情况下,惰性点匹配模式仍然会消耗大量内存。最好的办法是使用unroll-the-loop method

<Data>(?<data>[^<]*(?:<(?!/?Data>)[^<]*)*)</Data>

regex demo

详情

  • &lt;Data&gt; - 文字 &lt;Data&gt;
  • (?&lt;data&gt; - 捕获组“数据”的开始
    • [^&lt;]* - 除&lt; 之外的零个或多个字符
    • (?:&lt;(?!/?Data&gt;)[^&lt;]*)* - 0 个或多个序列:
      • &lt;(?!/?Data&gt;) - 一个 &lt;,后面没有 Data&gt;/Data&gt;
      • [^&lt;]* - 除&lt; 之外的零个或多个字符
  • ) - “数据”组结束
  • &lt;/Data&gt; - 结束分隔符

【讨论】:

  • 是的,根据我上面的示例,没有必要进行交替,因为 \r 和 \n 可以被 (?s) 指令替换。您的答案中的两种选择都按预期工作。谢谢和+1。 :-)
  • 我对更简单的正则表达式也有同样的问题: (.|\s)* 你能解释一下我如何避免 SO pls 吗?我正在尝试创建一个模式来匹配任何字符,包括行终止符等。Thx
  • @Toby 和这里一样。或者,只需将 (.|\s)* 替换为 (?s:.*)
  • @WiktorStribiżew 谢谢,这很好!我尝试了(?s)(.+?)*,它推迟了问题,但更大的输入仍然会如此。救生员!
  • @Toby 您需要为您的输入使用适当的解析器(如果是 (X)HTML 或 JSON,或类似的),或者确保您 unroll 惰性点模式。
猜你喜欢
  • 2013-07-05
  • 2019-08-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多