2012-08-09 13 views
5

Sto usando un'espressione regolare per estrarre le coppie chiave-valore da stringhe di input arbitrariamente lunghe e sono state eseguite in un caso in cui, per una stringa lunga con pattern ripetitivi, provoca uno stack overflow.Java Pattern causa overflow dello stack

Il codice KV-analisi sembra qualcosa di simile:

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

Alcuni esempi fittizi di uscita:

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

Calling parse(input1) produce:

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

Calling parse(input2) produce:

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

Questo va bene (anche con un po 'di elaborazione di stringhe richiesta per il primo caso). Tuttavia, quando si cerca di analizzare un tempo molto lungo (lunga più di 1000 caratteri) stringa di percorso di classe, si verifica il suddetto troppo pieno di classe, con la seguente eccezione (inizio):

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

La stringa è troppo lungo per mettere qui, ma ha la seguente struttura, facilmente riproducibile e ripetitivo:

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

Chiunque voglia riprodurre il problema ha solo bisogno di aggiungere :/opt/files/any qualche decina di volte alla stringa sopra. Dopo aver creato una stringa con circa 90 copie di ":/opt/files/any" presenti nella stringa classpath, si verifica l'overflow dello stack.

Esiste un modo generico per modificare la stringa KV_REGEX, in modo che il problema non si verifichi e vengano prodotti gli stessi risultati?

Ho inserito esplicitamente il metodo sopra, al contrario degli hack che (ad esempio) controllano la lunghezza massima della stringa prima dell'analisi.

La correzione più lordo potevo venire con, un vero e proprio anti-modello, è

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

Stranamente, funziona in un paio di basi ho provato, ma non è qualcosa di abbastanza piacevole, sia per consigliare . :-)

+2

Congratulazioni per aver superato i limiti. – kosa

+0

Grazie! Accetterei una soluzione per un premio in qualsiasi momento! :-) Cos'era esattamente il limite rotto? – PNS

+1

A cosa dovrebbe corrispondere questa parte? Non sembra affatto corretto. '[^ =,^\\) ^]'. – Keppil

risposta

3

La tua espressione regolare è eccessivamente complicata, ad esempio penso che tu non abbia ben capito come funzionano le classi di caratteri.Questo funziona meglio per me, non riesco a farlo traboccare più:

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

per abbattere l'espressione regolare, questo corrisponderà:

(\"[^\" ]*\"|[^{=, ]*): Tutto ciò racchiuso con " s, o un qualsiasi numero di non- {=, caratteri

*= *: zero a qualsiasi numero di spazi, seguita da =, seguite da zero a qualsiasi numero di spazi

(\"[^\"]*\"|[^=,) }]*): Qualsiasi oggetto incluso con " s o qualsiasi numero di caratteri non =,) }

+0

Questo sembra davvero meglio. Lo proverò con i miei casi molto più complessi prima possibilità. Grazie! :-) – PNS

Problemi correlati