2012-08-09 6 views
5

Ich verwende einen regulären Ausdruck, um Schlüssel-Wert-Paare aus beliebig langen Eingabezeichenfolgen zu extrahieren, und habe einen Fall ausgelöst, in dem für eine lange Zeichenfolge mit sich wiederholenden Mustern ein Stapelüberlauf verursacht wird.Java-Muster verursacht Stapelüberlauf

Der KV-Parsing-Code sieht etwa so aus:

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

Einige fiktive Beispiele für Ausgabe:

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) Aufruf erzeugt:

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

parse(input2) Aufruf erzeugt:

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

Dies ist in Ordnung (sogar mit ein wenig String-Verarbeitung für den ersten Fall erforderlich). Wenn jedoch einen sehr langen (mehr als 1.000 Zeichen) Classpath Zeichenfolge, die zuvor erwähnte Klasse Überlauf auftritt, mit folgenden Ausnahme zu analysieren versuchen (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) 
    ... 

Die Zeichenfolge zu lang sind hier zu setzen, aber es hat die folgende, leicht reproduzierbar und sich wiederholende Struktur:

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

wer die Frage muss nur anhängen :/opt/files/any ein paar Dutzend mal auf die obige Zeichenfolge reproduzieren will. Nach dem Erstellen einer Zeichenfolge mit ungefähr 90 Kopien von ":/opt/files/any" in der Klassenpfadzeichenfolge tritt der Stapelüberlauf auf.

Gibt es eine allgemeine Möglichkeit, dass die obige KV_REGEX Zeichenfolge geändert werden könnte, so dass das Problem nicht auftritt und die gleichen Ergebnisse erzeugt werden?

Ich lege explizit generische oben, im Gegensatz zu Hacks, die (zum Beispiel) vor dem Analysieren nach einer maximalen Stringlänge suchen.

Die Brutto fix ich mit, einem echten anti-Muster kommen konnte, ist

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

Komischerweise es in ein paar Runs arbeitet ich es versucht, aber es ist nicht etwas, geschmackvoll genug zu empfehlen . :-)

+2

Herzlichen Glückwunsch zum Überschreiten der Grenzen. – kosa

+0

Danke! Ich würde jederzeit eine Lösung für eine Belohnung akzeptieren! :-) Was genau war das Limit gebrochen? – PNS

+1

Was soll dieser Teil zusammenpassen? Es sieht überhaupt nicht korrekt aus. '[^ =,^\\) ^]'. – Keppil

Antwort

3

Ihre Regex sieht zu kompliziert aus, zum Beispiel, ich denke, Sie haben nicht ganz verstanden, wie Zeichenklassen arbeiten.Das funktioniert besser für mich, ich kann es nicht mehr überlaufen machen:

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

zu brechen, wird dieses Spiel:

(\"[^\" ]*\"|[^{=, ]*): Alles eingeschlossen mit " s, oder einem beliebigen Anzahl von Nicht- {=, Zeichen

*= *: Null auf eine beliebige Anzahl von Räumen, gefolgt von =, gefolgt von einer Null auf eine beliebige Anzahl von Räumen

(\"[^\"]*\"|[^=,) }]*): Alles im Lieferumfang enthalten " s, oder eine beliebige Anzahl von nicht =,) } Zeichen

+0

Das sieht tatsächlich besser aus. Ich werde es mit meinen viel komplexeren Fällen erstmal probieren. Vielen Dank! :-) – PNS