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

问题描述 投票:0回答:1

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

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

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

Data
):

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

上面的正则表达式与

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

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

java regex stack-overflow
1个回答
7
投票

有关堆栈溢出问题的起源的更多详细信息

有时正则表达式

Pattern

 类会抛出 
StackOverflowError
。这是 
已知错误 #5050507 的表现,该错误自 Java 1.4 以来一直存在于 java.util.regex
 包中。该错误会一直存在,因为它处于“无法修复”状态。发生此错误的原因是 
Pattern
 类将正则表达式编译成一个小程序,然后执行该程序以查找匹配项。该程序是递归使用的,有时当递归调用太多时会出现此错误。有关更多详细信息,请参阅
错误描述看来主要是通过使用交替来触发的。

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

您可以使用带有

Pattern.DOTALL

 修饰符(或等效的嵌入标志 
(?s)
)的惰性点匹配模式,这将使 
.
 也匹配换行符:

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

参见

这个正则表达式演示

但是,在输入量巨大的情况下,惰性点匹配模式仍然会消耗大量内存。最好的方法是使用

unroll-the-loop 方法:

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

查看

正则表达式演示

详情

  • <Data>
     - 文字文本 
    <Data>
    
    
  • (?<data>
     - 捕获组“数据”的开始
    
    
    • [^<]*
       - 除 
      <
       之外的零个或多个字符
      
    • (?:<(?!/?Data>)[^<]*)*
       - 0 个或多个序列:
      
      
      • <(?!/?Data>)
         - 后面不跟 
        <
        Data>
        /Data>
      • [^<]*
         - 除 
        <
         之外的零个或多个字符
        
  • )
     - “数据”组结束
  • </Data>
     - 结束分隔符
© www.soinside.com 2019 - 2024. All rights reserved.