用于匹配开始/结束标记的Java正则表达式会导致堆栈溢出

Java Pattern类的标准实现使用递归来实现许多forms的正则表达式(例如,某些运算符,交替)。

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

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

 (?(?:.|\r|\n)+?) 

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

是否可以重写上述正则表达式以避免堆栈溢出问题?

关于堆栈溢出问题的原因的更多细节:

有时,正则表达式Pattern类将抛出StackOverflowError 。 这是已知错误#5050507的一种表现forms,自Java 1.4以来一直存在于java.util.regex包中。 这个bug就在这里,因为它有“不会修复”的状态。 发生此错误是因为Pattern类将正则表达式编译为一个小程序,然后执行该程序以查找匹配项。 此程序是递归使用的,有时当进行过多的递归调用时会发生此错误。 有关更多详细信息,请参阅错误说明。 它似乎主要是由交替使用引发的。

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

您可以使用延迟点匹配模式与Pattern.DOTALL修饰符(或等效的嵌入标志(?s) )来制作. 匹配换行符号:

 (?s)(?.+?) 

看到这个正则表达式演示

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

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

请参阅正则表达式演示

细节

  • – 文字文本
  • (? – 捕获组“数据”的开始
    • [^<]* - 除<以外的零个或多个字符
    • (?:<(?!/?Data>)[^<]*)* - 0或更多序列:
      • <(?!/?Data>) - 一个<未跟随Data>/Data>
      • [^<]* - 除<以外的零个或多个字符
  • ) - “数据”组的结束
  • - 关闭分隔符