Java Pattern导致堆栈溢出

PNS*_*PNS 5 java regex stack-overflow pattern-matching key-value

我使用正则表达式从任意长的输入字符串中提取键值对,并遇到一种情况,对于具有重复模式的长字符串,它会导致堆栈溢出.

KV解析代码看起来像这样:

public static void parse(String input)
{
    String KV_REGEX = "((?:\"[^\"^ ]*\"|[^=,^ ])*) *= *((?:\"[^\"]*\"|[^=,^\\)^ ])*)";
    Pattern KV_PATTERN = Pattern.compile(KV_REGEX);

    Matcher matcher = KV_PATTERN.matcher(input);

    System.out.println("\nMatcher groups discovered:");

    while (matcher.find())
    {
        System.out.println(matcher.group(1) + ", " + matcher.group(2));
    }
}
Run Code Online (Sandbox Code Playgroud)

一些虚构的输出示例:

    String input1 = "2012-08-09 09:10:25,521 INFO com.a.package.SomeClass - Everything working fine {name=CentOS, family=Linux, category=OS, version=2.6.x}";
    String input2 = "2012-08-09 blah blah 09:12:38,462 Log for the main thread, PID=5872, version=\"7.1.8.x\", build=1234567, other=done";
Run Code Online (Sandbox Code Playgroud)

通话parse(input1)产生:

{name, CentOS
family, Linux
category, OS
version, 2.6.x}
Run Code Online (Sandbox Code Playgroud)

通话parse(input2)产生:

PID, 5872
version, "7.1.8.x"
build, 1234567
other, done
Run Code Online (Sandbox Code Playgroud)

这很好(即使第一种情况需要一些字符串处理).但是,当尝试解析很长(超过1,000个字符长)的类路径字符串时,会发生上述类溢出,并出现以下异常(start):

Exception in thread "main" java.lang.StackOverflowError
    at java.util.regex.Pattern$BitClass.isSatisfiedBy(Pattern.java:2927)
    at java.util.regex.Pattern$8.isSatisfiedBy(Pattern.java:4783)
    at java.util.regex.Pattern$8.isSatisfiedBy(Pattern.java:4783)
    at java.util.regex.Pattern$8.isSatisfiedBy(Pattern.java:4783)
    at java.util.regex.Pattern$8.isSatisfiedBy(Pattern.java:4783)
    at java.util.regex.Pattern$CharProperty.match(Pattern.java:3345)
    ...
Run Code Online (Sandbox Code Playgroud)

字符串太长而无法放在这里,但它具有以下易于重现和重复的结构:

java.class.path=/opt/files/any:/opt/files/any:/opt/files/any:/opt/files/any
Run Code Online (Sandbox Code Playgroud)

任何想要重现问题的人只需要:/opt/files/any在上面的字符串中附加几十次.在类路径字符串中创建一个包含大约90个":/ opt/files/any"副本的字符串后,会发生堆栈溢出.

是否有一种通用的方法KV_REGEX可以修改上面的字符串,以便不会发生问题并产生相同的结果?

我明确地将泛型放在上面,而不是在解析之前(例如)检查最大字符串长度的黑客.

我能提出的最严格的解决方案,一个真正的反模式,是

public void safeParse(String input)
{
    try
    {
        parse(input);
    }
    catch (StackOverflowError e) // Or even Throwable!
    {
        parse(input.substring(0, MAX_LENGTH));
    }
}
Run Code Online (Sandbox Code Playgroud)

有趣的是,它在我尝试过的几次运行中起作用,但它并不适合推荐.:-)

Kep*_*pil 3

您的正则表达式看起来过于复杂,例如,我认为您还不太了解字符类的工作原理。这对我来说效果更好,我不能再让它溢出了:

public static void parse(String input) {
    String KV_REGEX = "(\"[^\" ]*\"|[^{=, ]*) *= *(\"[^\"]*\"|[^=,) }]*)";
    Pattern KV_PATTERN = Pattern.compile(KV_REGEX);

    Matcher matcher = KV_PATTERN.matcher(input);

    System.out.println("\nMatcher groups discovered:");

    while (matcher.find()) {
        System.out.println(matcher.group(1) + ", " + matcher.group(2));
    }
}
Run Code Online (Sandbox Code Playgroud)

要分解正则表达式,这将匹配:

(\"[^\" ]*\"|[^{=, ]*):用"s 括起来的任何内容,或任意数量的非{=,字符

*= *:零到任意数量的空格,后跟=,后跟零到任意数量的空格

(\"[^\"]*\"|[^=,) }]*):用"s 括起来的任何内容,或任意数量的非=,) }字符