为什么java的正则表达式有时会抛出stackoverflow异常?

时间:2017-07-01 16:38:23

标签: java regex stack-overflow

我正在尝试为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),它抛出错误,稍后再次测试它,它运行良好。

2 个答案:

答案 0 :(得分:0)

我认为Java在某些情况下非常不喜欢轮换 哪里有潜在的回溯问题。

因此,此部分(\s(.*?))*?会在回溯中造成撤消负担 机制。

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

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

此外,此部分((/>)|>)可以缩减为(/?>),无需使用 通过交替换另一个堆栈帧。

总的来说,你并不真正需要捕获组。

如果你只需要解析标签,这是html的初始级别 解析,然后使用正则表达式是好的。

如果您想要解析单个标记,则需要DOM解析器。

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

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

"<(?:(?:(?:(script|style|object|embed|applet|noframes|noscript|noembed)(?:\\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]*?))))>"

扩展

 <
 (?:
      (?:
           (?:
                # 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]*? )
           )
      )
 )
 >

答案 1 :(得分:-1)

在我输入的基础上我测试过它工作正常,我无法重现错误,这里是实现:

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