Javascript Regex hängt (mit v8)

8

Ich verwende diese Regex, um den Inhalt eines Tags in einer Datei abzurufen.

%Vor%

Das verursacht, dass die v8-Engine unbegrenzt hängt.

Wenn ich nun new RegExp("<tag:main>([\s\S]*)</tag:main>") verwende, ist alles gut.

Hat jemand eine Idee, warum der erste zu lange dauert?

    
Engwan 09.03.2010, 09:25
quelle

3 Antworten

15

Diese katastrophale Rückverfolgung bei langen Folgen von Leerzeichen nach dem letzten schließenden </tag:main> -Tag. Betrachten Sie den Fall, in dem die Betreffzeile mit 100 Leerzeichen endet. Zuerst stimmt sie alle mit dem . auf der linken Seite der Abwechslung ab. Das scheitert, weil es kein schließendes Tag gibt, also versucht es, das letzte Zeichen mit dem \s zu vergleichen. Das scheitert auch, also versucht es, das vorletzte Leerzeichen als \s und das letzte Leerzeichen als . zu vergleichen. Das scheitert (immer noch kein schließendes Tag), also versucht es das letzte Leerzeichen als \s . Wenn dies fehlschlägt, stimmt es mit dem drittletzten Leerzeichen als \s überein und versucht alle 4 Möglichkeiten, die letzten beiden Leerzeichen zu finden. Wenn das fehlschlägt, versucht es das viertletzte Leerzeichen als \s und alle 8 Wege auf den letzten 3 Leerzeichen. Dann 16, 32 usw. Das Universum endet, bevor es zum 100.letzten Raum kommt.

Unterschiedliche VMs haben unterschiedliche Reaktionen auf Regexp-Matches, die aufgrund von katastrophalem Backtracking ewig dauern. Einige werden einfach "keine Übereinstimmung" melden. In V8 ist es wie das Schreiben einer anderen unendlichen oder nahezu unendlichen Schleife.

Die Verwendung von nicht-gierigem * wird tun, was Sie wollen (Sie wollen bei der ersten </tag:main> stoppen, nicht bei der letzten), aber katastrophisches Backtracking für lange Leerzeichenfolgen, bei denen die Closing-Sequenz fehlt.

Wenn Sie sicherstellen, dass die gleichen Zeichen in der inneren Klammer nicht mit beiden Seiten der Änderung übereinstimmen, wird das Problem von einer exponentiellen Eins zu eins reduziert, die in der Länge der Zeichenfolge linear ist. Verwenden Sie eine Zeichenklasse anstelle einer Alternation oder setzen Sie \n auf der rechten Seite der Alternationsleiste. \n ist nicht mit . identisch. Wenn Sie also eine lange Folge von Leerzeichen drücken, versucht die Regexp-Engine nicht alle Links-Rechts-Links-Kombinationen vor dem Beenden.

    
Erik Corry 09.03.2010, 11:23
quelle
3

Ich nehme an, dass es katastrophisch zurück verfolgt.

Ich denke, dass ein Teil des Problems gut sein kann, dass sich der Punkt und \ s nicht gegenseitig ausschließen.

Wenn ich den Ausdruck in

ändere %Vor%

und führen Sie es im Regex Buddy Debugger aus es schlägt sehr viel schneller fehl, wenn die Testzeichenfolge nicht übereinstimmt.

    
Martin Smith 09.03.2010 09:32
quelle
0

Anstelle von (?:.|\s)* können Sie [^]* verwenden, um ein beliebiges Zeichen einschließlich verschiedener Formen von Zeilenumbruch zu finden.

Es gibt keine Abwechslung, also kein Risiko eines katastrophalen Backtracking.

    
Jim Shark 07.02.2015 15:37
quelle

Tags und Links