Über das Phänomen, dass StackOverflowError bei der Verarbeitung mit regulären Java-Ausdrücken auftritt

Bestätigte Umgebung

Phänomen

Ein StackOverflowError tritt auf, wenn ein langer Satz verarbeitet wird, der einen bestimmten regulären Ausdruck erfüllt.

Test.java


public class Test {
    public static void main(final String[] args) {
        final StringBuilder input = new StringBuilder();
        for (int i = 0; i < 5000; i++) {
            input.append("a ");
        }
        System.out.println(input.toString().replaceAll("(a|\\s)+", ""));
    }
}
Exception in thread "main" java.lang.StackOverflowError
	at java.base/java.util.regex.Pattern$GroupHead.match(Pattern.java:4786)
	at java.base/java.util.regex.Pattern$Loop.match(Pattern.java:4923)
	at java.base/java.util.regex.Pattern$GroupTail.match(Pattern.java:4845)
	at java.base/java.util.regex.Pattern$BranchConn.match(Pattern.java:4695)
...

Ursache

  1. Reguläre Ausdrücke werden als rekursive Struktur behandelt
  2. Rekursive Methodenaufrufe werden fortgesetzt, solange die zu verarbeitende Zeichenfolge die Bedingung erfüllt.

Ursache Details

Erstens ist der reguläre Ausdruck, der als Zeichenfolge angegeben wird, das Muster der inneren Klasse von [Muster](https://docs.oracle.com/javase/jp/9/docs/api/java/util/regex/Pattern.html). Es wird in Unterklassen von $ Node zerlegt. Dies dient zur Verarbeitung von Pattern.compile Äquivalent. Beispielsweise hat der oben erwähnte reguläre Ausdruck "(a | \ s) +" die folgende Struktur.

Start
  ---> Prolog
      ---> Loop  "+"
           ---> GroupHead "("
                 ---> Branch "|"
                     ---> BmpCharProperty "a"
                           ---> BranchConn
                                ---> GroupTail
                     ---> BmpCharProperty "\\s"
                           ---> BranchConn
                                ---> GroupTail
           ---> GroupTail ")"
                 ---> Loop

Dann wird gemäß der interpretierten Struktur bestätigt, ob die Bedingung vom Beginn der zu verarbeitenden Zeichenfolge nacheinander erfüllt ist, und die Beurteilungsmethode wird rekursiv aufgerufen, solange die Bedingung erfüllt ist.

  1. Loop
  2. GroupHead
  3. Branch
  4. BmpCharProperty
  5. BranchConn
  6. GroupTail
  7. Loop
  8. ... Wiederholen

Reproduktionsmuster

Wiedergabe mit den folgenden Kombinationen.

Zum Beispiel haben reguläre Ausdrücke wie (a \\ s?) + Das gleiche Problem.

Gegenmaßnahmen

Verwenden Sie die Zeichenklasse

Wenn Sie nicht in Klammern gruppieren müssen, erstellen Sie eine ODER-Bedingung mit der Zeichenklasse (eckige Klammern). Wenn Sie im Beispiel "[a \ s] +" setzen, tritt kein Fehler auf.

Verwenden Sie nicht das Quantum mit der längsten Übereinstimmungsnummer

Für Muster, die nur durch Leerzeichen ersetzt werden, ist die zu vergleichende Einheit nicht besonders wichtig. Selbst wenn Sie einfach "a | \ s" ausführen, ohne "*" oder "+" (das längste Übereinstimmungsnummernquantum) anzugeben, sind alle Sie können Zeichen ersetzen (Verarbeitungszeit um einige Millisekunden erhöht).

Oder das kürzeste Übereinstimmungsnummernquantum(a|\\s)+?Oder ein gieriges Zahlenquantum(a|\\s)++Wenn Sie verwenden, wird es als eine andere Struktur interpretiert, sodass kein Fehler auftritt.

Bemerkungen

Seien Sie vorsichtig, außer replaceAll

Da es sich um ein strukturelles Problem handelt, das einen regulären Ausdruck darstellt, tritt ein ähnliches Problem bei der Verarbeitung auf, bei der ein regulärer Ausdruck verwendet wird.

String

Matcher

Verhalten in Android-Apps

Die Android-App hatte dieses Problem nicht. Die Java-Implementierung basiert auf OpenJDK und reguläre Ausdrücke basieren auf nativem Code von icu Es ist implementiert und ist für eine ganz andere Sache.

Recommended Posts

Über das Phänomen, dass StackOverflowError bei der Verarbeitung mit regulären Java-Ausdrücken auftritt
Passen Sie IP-Adressen mithilfe regulärer Ausdrücke in Java an
Über reguläre Ausdrücke in Ruby
Informationen zur Dateikopierverarbeitung in Java
Versuchen Sie es mit der Stream-API in Java
ERRORCODE = -4471 tritt in einer Java-Anwendung auf, die Db2 verwendet.
Über die Verwirrung beim Starten von Java-Servern
Über die Idee anonymer Klassen in Java
ChatWork4j für die Verwendung der ChatWork-API in Java
Eine Geschichte über das JDK in der Java 11-Ära
Eindrücke und Zweifel an der erstmaligen Verwendung von Java in Android Studio
Zeigen Sie "Hello World" im Browser mit Java an
Versuchen Sie es mit der Syntaxanalyse der COTOHA-API in Java
Informationen zum regulären Ausdruck, der in der Ruby-Submethode verwendet wird
[Java] Sortieren Sie die Liste mit Streams und Lambda-Ausdrücken
Über das Problem des Deadlocks bei der Parallelverarbeitung in gem'sprockets '4.0
[Java] Erläutert die ConcurrentModificationException, die in java.util.ArrayList für Neulinge auftritt
Versuchen Sie es mit globalem Hooking in Java mithilfe der JNativeHook-Bibliothek
Code, der nur die in der Verarbeitung integrierte Kamera anzeigt
Denken Sie an das JAVA = JAVAscript-Problem (wird in Zukunft benötigt).
Unterschiede im Code bei Verwendung des Längensystems in Java
Hinweise zur Verwendung regulärer Ausdrücke in Java
Die Geschichte, dass .java auch in Unity 2018 erstellt wurde
Über Java-Lambda-Ausdrücke
Erfahren Sie mehr über Spezifikationen, während Sie FizzBuzz verkürzen, das in Java geschrieben wurde
[Java] Vergleichsmethode für Zeichenketten und Vergleichsmethode mit regulären Ausdrücken
Identifizieren Sie Threads im Java-Prozess, die CPU verschwenden
Denken Sie über die Unterschiede zwischen Funktionen und Methoden nach (in Java)
Die Geschichte der Verwendung von Java Input Waiting (Scanner) mit VSCode
Tastenkürzel, der die Standardausgabeverarbeitung in IDE ergänzt
Versuchen Sie es mit RocksDB mit Java
[Java] Zusammenfassung der regulären Ausdrücke
Gemessene Parallelverarbeitung mit Java
Über Java Abstract Class
[Java] Abrufen von Datumsinformationen 10 Tage später in Millisekunden in der Datumsklasse
[Java] Versuchen Sie, das Fizz Buzz-Problem mithilfe der rekursiven Verarbeitung zu lösen
So beheben Sie den unbekannten Fehler, der bei der Verwendung von slf4j in Java aufgetreten ist
Beispielcode mit JMustache, der Moustache-Vorlagen-Engine in Java