【问题标题】:Why does java's Regex throws stackoverflow exception sometimes?为什么java的Regex有时会抛出stackoverflow异常?
【发布时间】:2017-07-01 16:38:22
【问题描述】:

我正在尝试为 HTML 标签制作正则表达式。 到目前为止我创建的正则表达式是<(/?)(\w+?)(\s(.*?))*?((/>)|>),当我在线测试它时,它运行良好;但是当我使用 Java 正则表达式对其进行测试时,它有时会抛出 StackOverFlowError 有时不会。

我正在使用此代码进行测试:

public static void parseHtml(String urlString){
    new Thread(new Runnable() {
        @Override
        public void run() {
            int count = 0;
            int count2 = 0;
            String htmlScript = downloadWebPage(urlString);
            Matcher matcher = Pattern.compile("<(/?)(\\w+?)(\\s(.*?))*?((/>)|>)",
                                              Pattern.DOTALL).matcher(htmlScript);
            while(matcher.find()) {
                System.out.println(matcher.group());
            }
        }
    }).start();
}

所以,我的问题是: 为什么 Java 的正则表达式引擎有时会抛出 StackOverFlowError 有时不会?

注意:我使用了相同的测试输入(相同的 URL),它抛出了错误,稍后再次测试它,它运行良好。

【问题讨论】:

  • 你读过this post吗?有关内存空间的错误,例如 StackOverflowErrorOutOfMemoryException,通常是不可预测的,并且取决于清理过程。
  • 你得到 StackOverFlowError 的标签/文件是什么?你能发布你的测试文件吗?
  • 感谢您的评论,不,但我已经阅读了许多关于正则表达式及其工作原理的帖子,并且我试图使正则表达式尽可能贪婪以最小化递归过程,除了正则表达式的引擎,我没有使用任何额外的递归调用,我在这里遗漏了什么吗?
  • @MiguelTorres 我将一个网址传递给方法前:Link
  • @MCEmperor 那么有没有办法将这个错误的可能性降到最低?

标签: java regex stack-overflow


【解决方案1】:

我认为众所周知,Java 在某些情况下不喜欢交替
存在潜在回溯问题的地方。

所以,这部分 (\s(.*?))*? 在回溯上产生了撤消负担
机制。

 (                             # (3 start)
      \s
      ( .*? )                       # (4)
 )*?                           # (3 end)

最终结果是嵌套的可选量词。
它可以减少到([\S\s]*?) 而不会出现嵌套问题。

另外,这部分 ((/&gt;)|&gt;) 可以减少到 (/?&gt;) 消除需要
通过交替获取另一个堆栈帧。

总的来说,您并不需要捕获组。


如果只需要解析标签,也就是html的初始级别
解析,然后使用正则表达式就可以了。

如果您想做的不仅仅是解析单个标签,则需要一个 DOM 解析器。

我发现这个正则表达式会解析所有单独的 html/xml 标签。

https://regex101.com/r/YXhCxe/1

"&lt;(?:(?:(?:(script|style|object|embed|applet|noframes|noscript|noembed)(?:\\s+(?&gt;\"[\\S\\s]*?\"|'[\\S\\s]*?'|(?:(?!/&gt;)[^&gt;])?)+)?\\s*&gt;)[\\S\\s]*?&lt;/\\1\\s*(?=&gt;))|(?:/?[\\w:]+\\s*/?)|(?:[\\w:]+\\s+(?:\"[\\S\\s]*?\"|'[\\S\\s]*?'|[^&gt;]?)+\\s*/?)|\\?[\\S\\s]*?\\?|(?:!(?:(?:DOCTYPE[\\S\\s]*?)|(?:\\[CDATA\\[[\\S\\s]*?\\]\\])|(?:--[\\S\\s]*?--)|(?:ATTLIST[\\S\\s]*?)|(?:ENTITY[\\S\\s]*?)|(?:ELEMENT[\\S\\s]*?))))&gt;"

展开

 <
 (?:
      (?:
           (?:
                # Invisible content; end tag req'd
                (                             # (1 start)
                     script
                  |  style
                  |  object
                  |  embed
                  |  applet
                  |  noframes
                  |  noscript
                  |  noembed 
                )                             # (1 end)
                (?:
                     \s+ 
                     (?>
                          " [\S\s]*? "
                       |  ' [\S\s]*? '
                       |  (?:
                               (?! /> )
                               [^>] 
                          )?
                     )+
                )?
                \s* >
           )

           [\S\s]*? </ \1 \s* 
           (?= > )
      )

   |  (?: /? [\w:]+ \s* /? )
   |  (?:
           [\w:]+ 
           \s+ 
           (?:
                " [\S\s]*? " 
             |  ' [\S\s]*? ' 
             |  [^>]? 
           )+
           \s* /?
      )
   |  \? [\S\s]*? \?
   |  (?:
           !
           (?:
                (?: DOCTYPE [\S\s]*? )
             |  (?: \[CDATA\[ [\S\s]*? \]\] )
             |  (?: -- [\S\s]*? -- )
             |  (?: ATTLIST [\S\s]*? )
             |  (?: ENTITY [\S\s]*? )
             |  (?: ELEMENT [\S\s]*? )
           )
      )
 )
 >

【讨论】:

  • 感谢您的回答,这真的很有帮助,我根据您的回答对我的正则表达式进行了一些修改,这就是我目前得到的&lt;(/?)(\w+?)([\S\s]*?)(/?&gt;),我正在使用括号来测试组。
【解决方案2】:

根据您的输入,我测试了它,它工作正常,我无法重现错误,这里是实现:

public static void main(String[] args) {
    new Thread(new Runnable() {
        @Override
        public void run() {
            String htmlScript = downloadWebPage("https://stackoverflow.com/questions/13684468/java-runnable-run-method-returning-a-value");
            Matcher matcher = Pattern.compile("<(/?)(\\w+?)(\\s(.*?))*?((/>)|>)",
                                              Pattern.DOTALL).matcher(htmlScript);
            while(matcher.find()) {
                System.out.println(matcher.group());
            }
        }
    }).start();

}

private static String downloadWebPage(String urlString) {
    StringBuilder sb = new StringBuilder();
    try {
        URL u = new URL(urlString);
        BufferedReader in = new BufferedReader(new InputStreamReader(u.openStream()));

        String inputLine;
        while ((inputLine = in.readLine()) != null) {
            sb.append(inputLine);
        }
        in.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
    return sb.toString();
}

这是输出:https://pastebin.com/s9DbBVBJ

【讨论】:

  • 感谢您的努力,我会尝试重现错误并发布它
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-11
  • 1970-01-01
  • 2021-04-28
  • 2013-09-04
相关资源
最近更新 更多