5

正規表現を使用して、任意の長さの入力文字列からキーと値のペアを抽出していますが、繰り返しパターンのある長い文字列の場合、スタックオーバーフローが発生する場合があります。

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));
    }
}

出力のいくつかの架空の例:

    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";

呼び出すと次のようになりparse(input1)ます。

{name, CentOS
family, Linux
category, OS
version, 2.6.x}

呼び出すと次のようになりparse(input2)ます。

PID, 5872
version, "7.1.8.x"
build, 1234567
other, done

これは問題ありません(最初のケースで少しの文字列処理が必要な場合でも)。ただし、非常に長い(1,000文字を超える)クラスパス文字列を解析しようとすると、次の例外(開始)を除いて、前述のクラスオーバーフローが発生します。

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)
    ...

文字列は長すぎてここに配置できませんが、次のように、簡単に再現でき、反復的な構造になっています。

java.class.path=/opt/files/any:/opt/files/any:/opt/files/any:/opt/files/any

問題を再現したい人は:/opt/files/any、上記の文字列に数十回追加する必要があります。クラスパス文字列に「:/ opt / files / any」のコピーが約90個存在する文字列を作成すると、スタックオーバーフローが発生します。

KV_REGEX問題が発生せず、同じ結果が生成されるように、上記の文字列を変更できる一般的な方法はありますか?

解析する前に(たとえば)最大文字列長をチェックするハックとは対照的に、私は明示的にジェネリックを上に置きました。

私が思いつくことができる最も大まかな修正、真のアンチパターンは、

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

おかしなことに、それは私が試した数回の実行で動作しますが、それはお勧めするほど上品なものではありません。:-)

4

1 に答える 1

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));
    }
}

regexを分解するには、これは次のように一致します。

(\"[^\" ]*\"|[^{=, ]*):sで囲まれたもの"、または任意の数の非{=,文字

*= *:ゼロから任意の数のスペース、その後=にゼロから任意の数のスペース

(\"[^\"]*\"|[^=,) }]*):sで囲まれたもの"、または任意の数の非=,) }文字

于 2012-08-09T20:47:39.140 に答える