有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

使用正则表达式解析大字符串以获取java。lang.StackOverflower错误

这是我的正则表达式

"(?<=\"body\":\")((?=\",|\"$)|.)+"

它表示主体上的字符串。比如

"body":its my string

结果是

its my string

但是当我使用一个大的输入字符串时,我得到了这个错误

Exception in thread "main" java.lang.StackOverflowError
at java.util.regex.Pattern$GroupHead.match(Pattern.java:4168)
at java.util.regex.Pattern$Loop.match(Pattern.java:4295)
at java.util.regex.Pattern$GroupTail.match(Pattern.java:4227)
at java.util.regex.Pattern$BranchConn.match(Pattern.java:4078)
at java.util.regex.Pattern$CharProperty.match(Pattern.java:3345)
at java.util.regex.Pattern$Branch.match(Pattern.java:4114)
at java.util.regex.Pattern$GroupHead.match(Pattern.java:4168)
at java.util.regex.Pattern$Loop.match(Pattern.java:4295)
at java.util.regex.Pattern$GroupTail.match(Pattern.java:4227)

这是怎么造成的,我该如何解决


共 (1) 个答案

  1. # 1 楼答案

    看起来您试图捕获RHS字符串的内容,同时强制它前面是"body":",后面是"

    您似乎在使用lookaround断言来测试周围文本的存在,但也在使用捕获组来捕获RHS字符串的内容。你不需要同时做这两件事。Lookaround断言是零宽度,这意味着它们不会构成最终匹配的子字符串的一部分。最终匹配的子字符串始终可以作为捕获组0访问。或者,您可以完全匹配所有正则表达式组件(意味着非零宽度匹配,意味着没有lookarounds),并使用捕获组来提取感兴趣的子字符串,但这样效率较低

    我认为应该这样写(与本演示的args[0]相匹配):

    Pattern p = Pattern.compile("(?<=\"body\":\")[^\"]*(?=\")");
    Matcher m = p.matcher(args[0]);
    if (!m.find(0)) { System.out.println("doesn't match!"); System.exit(1); }
    System.out.println(m.group(0));
    

    以上内容对我来说非常有用


    我确实试图重现StackOverflowError异常,我成功了。在我看来,Java正则表达式引擎正在使用递归来实现重复替换的匹配。这让我非常惊讶,因为我根本不知道为什么需要递归来匹配重复的交替。话虽如此,我还对Perl正则表达式做了一个小测试,我一直认为它是目前最强大、最健壮的正则表达式风格,并进一步惊讶地发现Perl的失败方式与Java正则表达式完全相同

    下面是一个例子,展示了Java失败和Perl失败。在这个演示中,我将[^"]原子更改为(?:\\.|[^"])交替,这有效地增加了对嵌入双引号字符串中的反斜杠转义码的支持,例如\"对嵌入的双引号进行编码,这在许多编程环境中普遍受支持

    Java

    Pattern p = Pattern.compile("(?<=\"body\":\")(?:\\\\.|[^\"])*(?=\")");
    Matcher m = p.matcher(args[0]);
    if (!m.find(0)) { System.out.println("doesn't match!"); System.exit(1); }
    System.out.println(m.group(0));
    

    输出

    Exception in thread "main" java.lang.StackOverflowError
      at java.util.regex.Pattern$CharProperty.match(Unknown Source)
      at java.util.regex.Pattern$Branch.match(Unknown Source)
      at java.util.regex.Pattern$GroupHead.match(Unknown Source)
      at java.util.regex.Pattern$Loop.match(Unknown Source)
      at java.util.regex.Pattern$GroupTail.match(Unknown Source)
      at java.util.regex.Pattern$BranchConn.match(Unknown Source)
      at java.util.regex.Pattern$CharProperty.match(Unknown Source)
      at java.util.regex.Pattern$Branch.match(Unknown Source)
      at java.util.regex.Pattern$GroupHead.match(Unknown Source)
      at java.util.regex.Pattern$Loop.match(Unknown Source)
      at java.util.regex.Pattern$GroupTail.match(Unknown Source)
    ...
    

    Perl(来自shell)

    largeString="\"body\":\"$(perl -e 'use strict; use warnings; for (my $i = 0; $i < 2**15; ++$i) { print("x"); }';)\"";
    perl -e 'use strict; use warnings; my $re = qr((?<="body":")(?:\\.|[^"])*(?=")); if ($ARGV[0] !~ $re) { print("didn'\''t match!\n"); } print($&);' "$largeString";
    

    输出

    Complex regular subexpression recursion limit (32766) exceeded at -e line 1.
    didn't match!
    Use of uninitialized value $& in print at -e line 1.
    

    所以,我要澄清的是,我在答案开头给出的解决方案之所以避免了堆栈溢出错误,并不是因为我删除了捕获组1,而是因为我删除了替换。同样,我不知道为什么重复的交替是用递归实现的,但考虑到这一事实,一个大的输入字符串会导致堆栈溢出错误似乎是合乎逻辑的