À propos du phénomène que StackOverflowError se produit lors du traitement à l'aide d'expressions régulières Java

Environnement confirmé

phénomène

Une StackOverflowError se produit lors du traitement d'une longue phrase qui satisfait une expression régulière spécifique.

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

Cause

  1. Les expressions régulières sont traitées comme une structure récursive
  2. Les appels de méthode récursifs se poursuivent tant que la chaîne de caractères à traiter satisfait à la condition.

Détails de la cause

Tout d'abord, l'expression régulière donnée sous forme de chaîne de caractères est le Pattern de la classe interne de [Pattern](https://docs.oracle.com/javase/jp/9/docs/api/java/util/regex/Pattern.html). Il est décomposé en sous-classes de $ Node. Ceci est destiné au traitement de Pattern.compile Équivalent. Par exemple, l'expression régulière mentionnée ci-dessus (a | \\ s) + a la structure suivante.

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

Ensuite, selon la structure interprétée, il est confirmé si la condition est satisfaite une à une depuis le début de la chaîne de caractères à traiter, et la méthode de jugement est appelée récursivement tant que la condition est satisfaite.

  1. Loop
  2. GroupHead
  3. Branch
  4. BmpCharProperty
  5. BranchConn
  6. GroupTail
  7. Loop
  8. ... Répéter

Modèle de reproduction

Reproduit avec les combinaisons suivantes.

Par exemple, les expressions régulières comme (a \\ s?) + Posent le même problème.

Contre-mesures

Utiliser la classe de caractères

Si vous n'avez pas besoin de regrouper par parenthèses, créez une condition OR avec la classe de caractères (crochets). Dans le cas de l'exemple, si vous définissez [a \\ s] +, aucune erreur ne se produira.

N'utilisez pas le quantum de numéro de correspondance le plus long

Pour les motifs qui sont simplement remplacés par des blancs, l'unité à mettre en correspondance n'est pas particulièrement importante, donc même si vous simplement ʻa | \ ssans spécifier*ou+` (quantum de numéro de correspondance le plus long), tout Vous pouvez remplacer des caractères (temps de traitement augmenté de quelques millisecondes).

Ou le quantum de numéro de correspondance le plus court(a|\\s)+?Ou un quantum de nombre gourmand(a|\\s)++Si vous utilisez, il sera interprété comme une structure différente, donc aucune erreur ne se produira.

Remarques

Attention autre que de remplacer

Puisqu'il s'agit d'un problème structurel qui représente une expression régulière, un problème similaire se produit dans le traitement qui utilise une expression régulière.

String

Matcher

Comportement dans les applications Android

L'application Android n'a pas rencontré ce problème. L'implémentation Java est basée sur OpenJDK et les expressions régulières sont basées sur le code natif icu Il a été mis en œuvre et est pour une chose complètement différente.

Recommended Posts

À propos du phénomène que StackOverflowError se produit lors du traitement à l'aide d'expressions régulières Java
Faire correspondre les adresses IP à l'aide d'expressions régulières en Java
À propos des expressions régulières dans Ruby
A propos du traitement de la copie de fichiers en Java
Essayez d'utiliser l'API Stream en Java
ERRORCODE = -4471 se produit dans une application Java qui utilise Db2.
À propos de la confusion observée dans les serveurs Java de démarrage
À propos de l'idée des classes anonymes en Java
ChatWork4j pour l'utilisation de l'API ChatWork en Java
Une histoire sur le JDK à l'ère de Java 11
Impressions et doutes sur l'utilisation de Java pour la première fois dans Android Studio
Afficher "Hello World" dans le navigateur à l'aide de Java
Essayez d'utiliser l'analyse syntaxique de l'API COTOHA en Java
À propos de l'expression régulière utilisée dans la méthode ruby sub
[Java] Trier la liste à l'aide de flux et d'expressions lambda
À propos du problème de blocage dans le traitement parallèle dans la version 4.0 de gem'sprockets
[Java] explique ConcurrentModificationException qui se produit dans java.util.ArrayList pour les nouveaux arrivants
Essayez le hooking global en Java à l'aide de la bibliothèque JNativeHook
Code qui affiche uniquement la caméra intégrée dans le traitement
Pensez au problème JAVA = JAVAscript (nécessaire à l'avenir)
Différences de code lors de l'utilisation du système de longueur en Java
Remarques sur l'utilisation des expressions régulières en Java
L'histoire que .java est également construite dans Unity 2018
À propos des expressions Java lambda
Découvrez les spécifications tout en raccourcissant FizzBuzz écrit en Java
[Java] Méthode de comparaison de chaînes de caractères et méthode de comparaison utilisant des expressions régulières
Identifiez les threads du processus Java qui gaspillent du processeur
Pensez aux différences entre les fonctions et les méthodes (en Java)
L'histoire de l'utilisation de Java Input Wait (Scanner) avec VSCode
Touche de raccourci qui complète le traitement de sortie standard dans l'IDE
Essayez d'utiliser RocksDB avec Java
[Java] Résumé des expressions régulières
Traitement parallèle mesuré avec Java
À propos de la classe abstraite Java
[Java] Obtenez des informations sur la date 10 jours plus tard en utilisant la milliseconde dans la classe Date
[Java] Essayez de résoudre le problème de Fizz Buzz en utilisant un traitement récursif
Comment résoudre l'erreur inconnue apparue lors de l'utilisation de slf4j en Java
Exemple de code utilisant JMustache, le moteur de modèles Moustache en Java