Java PatternSyntaxException: Illégal de la répétition sur la chaîne de substitution?
Je suis en train d'écrire une méthode qui vous permettra d'accepter un String
, l'inspecter pour les instances de certains tokens (par exemple ${fizz}
, ${buzz}
, ${foo}
, etc.) et de remplacer chaque jeton avec une nouvelle chaîne qui est récupérée à partir d'un Map<String,String>
.
Par exemple, si je passe cette méthode de la chaîne de caractères suivante:
"Comment maintenant ${fizz} vache. Le ${buzz} avait curieusement en forme ${foo}."
Et si la méthode consulté le suivant Map<String,String>
:
Key Value
==========================
"fizz" "brown"
"buzz" "arsonist"
"foo" "feet"
Ensuite, la chaîne résultante serait:
"Comment maintenant noireaude. Le pyromane a curieusement en forme de pieds."
Voici ma méthode:
String substituteAllTokens(Map<String,String> tokensMap, String toInspect) {
String regex = "\$\\{([^}]*)\\}";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(toInspect);
while(matcher.find()) {
String token = matcher.group(); //Ex: ${fizz}
String tokenKey = matcher.group(1); //Ex: fizz
String replacementValue = null;
if(tokensMap.containsKey(tokenKey))
replacementValue = tokensMap.get(tokenKey);
else
throw new RuntimeException("String contained an unsupported token.");
toInspect = toInspect.replaceFirst(token, replacementValue);
}
return toInspect;
}
Lorsque je l'exécute, j'obtiens l'exception suivante:
Exception in thread "main" java.util.regex.PatternSyntaxException: Illegal repetition near index 0
${fizz}
^
at java.util.regex.Pattern.error(Pattern.java:1730)
at java.util.regex.Pattern.closure(Pattern.java:2792)
at java.util.regex.Pattern.sequence(Pattern.java:1906)
at java.util.regex.Pattern.expr(Pattern.java:1769)
at java.util.regex.Pattern.compile(Pattern.java:1477)
at java.util.regex.Pattern.<init>(Pattern.java:1150)
at java.util.regex.Pattern.compile(Pattern.java:840)
at java.lang.String.replaceFirst(String.java:2158)
...rest of stack trace omitted for brevity (but available upon request!)
Pourquoi suis-je cela? Et est-ce la bonne solution? Merci à l'avance!
OriginalL'auteur | 2013-07-04
Vous devez vous connecter pour publier un commentaire.
Dans
${fizz}
{
est un indicateur du moteur d'expressions régulières que vous êtes sur le point de commencer une répétition de l'indicateur, comme{2,4}
qui signifie "2 à 4 fois supérieure à la précédente jeton". Mais{f
est illégal, car il doit être suivi par un nombre, il lève une exception.Vous avez besoin pour échapper à tous les regex métacaractères (dans ce cas
$
,{
et}
) (essayez d'utiliser http://docs.oracle.com/javase/6/docs/api/java/util/regex/Pattern.html#quote(java.lang.Chaîne de caractères) ) ou utiliser une autre méthode qui remplace une chaîne de caractères une chaîne de caractères, pas un regex pour une chaîne de caractères.{
est échappé:"\\$\\{([^}]*)\\}"
Non, il n'est pas. La trace de pile:
at java.lang.String.replaceFirst(String.java:2158)
fait référence à cette ligne:toInspect = toInspect.replaceFirst(token, replacementValue);
Ettoken
's la valeur est${fizz}
, sans s'échapper.Ah, oui! Bien sûr. Lu trop vite, je suppose. +1 tu peux peut-être donner plus de détails sur ce qui est passé et pourquoi c'est un échec.
Exactement - j'ai encore des difficultés à mettre votre réponse dans le mouvement, mais merci @Patashu et +1!
s'attend à une expression régulière dans le premier argument, tout comme le
Pattern.compile
méthode. Cependant, vous êtes en passant il${fizz}
sans s'échapper. Utiliser lesPattern.quote(token)
avant de passertoken
.OriginalL'auteur
Comme l'a souligné Patashu, le problème est dans
replaceFirst(token, replacementValue)
, qui s'attend à une regex dans le premier argument, et non littéral. Changer dereplaceFirst(Pattern.quote(token), replacementValue)
et vous ferez bien.J'ai aussi changé un peu la première regex, que l'on va plus vite avec
+
au lieu de*
mais ce n'est pas nécessaire.[^}]++
à faire possessif au lieu de simplement avide de sorte qu'il ne sera jamais revenir en arrière.La vitesse de résultats sont surprenants: en utilisant ma méthode, l'original de la regex (
[^}]*
) a pris 37.04 ms pour exécuter 10000 fois, ma regex ([^}]+
) a pris 37.35 ms et a suggéré regex ([^}]++
) 36.98 mme. Presque pas de différence.Pour plus d'entrée
"How now ${fizz} cow. The ${buzz} had oddly-shaped ${foo}.How now ${fizz} cow. The ${buzz} had oddly-shaped ${foo}.${fizz}${foo}${buzz}${buzz}${foo}${fizz}${foo}${fizz}${buzz}${fizz}${foo}${buzz}${buzz}${foo}${fizz}${foo}${fizz}${buzz}${fizz}${foo}${buzz}${buzz}${foo}${fizz}${foo}${fizz}${buzz}"
les résultats étaient différents pour les regex. L'original regex ([^}]*
) a pris 515.47 ms pour exécuter 10000 fois, ma regex ([^}]+
) a pris 514.41 ms et regex suggéré par @Brian ([^}]++
) 507.41 mme. Encore proche, mais Brian a fait mieux.Avez-vous utilisé un réel outil d'analyse comparative pour vérifier, comme Étrier? Aussi, vous devriez assurez-vous de tester partiellement chaînes mises en correspondance, comme
The ${fizz cow
sans résiliation de}
. Aussi, pour être juste, je l'ai dit vraiment pointilleux :3Non, j'ai écrit le benchmarking moi-même. Fait une boucle d'échauffement au début pour activer le hotspot et appelé
System.gc()
à plusieurs reprises à l'extérieur de la mesure. Mais, oui, je sais, c'est pas l'épreuve des balles. Je ne savais pas à propos de l'Étrier. Bon à savoir!OriginalL'auteur
Adapté de
Matcher.replaceAll
L'ajout d'ici mes résultats de test à l'aide de cette méthode: l'original regex (
[^}]*
) a pris 15.14 ms pour exécuter 10000 fois, ma regex ([^}]+
) a pris 14.88 ms et regex suggéré par @Brian ([^}]++
) de 15,89 mme. Comme je l'ai dit avant, cette méthode est un vainqueur absolu en termes de vitesse et voici ma regex va mieux 🙂Essayez d'utiliser une très longue
toInspect
comme entrée, comme"${fizz}${buzz}${foo}${fizz}${buzz}${foo}${fizz}${buzz}${foo}...${fizz}${buzz}${foo}${fizz}${buzz}${foo}${fizz}${buzz}${foo}${fizz}${buzz}${foo}"
(je parie maStringBuffer
sera plus rapide que votreString
, lorsque la longueur de la croissance, il ne sera pas seulement 3x plus rapide.)À l'aide de
"How now ${fizz} cow. The ${buzz} had oddly-shaped ${foo}.How now ${fizz} cow. The ${buzz} had oddly-shaped ${foo}.${fizz}${foo}${buzz}${buzz}${foo}${fizz}${foo}${fizz}${buzz}${fizz}${foo}${buzz}${buzz}${foo}${fizz}${foo}${fizz}${buzz}${fizz}${foo}${buzz}${buzz}${foo}${fizz}${foo}${fizz}${buzz}"
que l'entrée de cette méthode exécutée même mieux que le mien, c'était 5 fois plus rapide. Les résultats étaient différents pour les regex. L'original regex ([^}]*
) a pris 92.94 ms pour exécuter 10000 fois, ma regex ([^}]+
) a pris de 93,6 ms et regex suggéré par @Brian ([^}]++
) 91.83 mme. Brian a fait mieux. Pourquoi donc?Je sais seulement environ 3x->5x: 1.
replaceFirst
utiliser une regex de nouveau, ce qui est inutile. 2. Dans une boucle,StringBuffer
concatsString
plus vite que directement dansString
, depuisString
générer un nouveauString
à chaque fois. Qui regex n'est pas important.OriginalL'auteur
Utilisez de la Ficelle-replaceAll.
Exemple de Chaîne d'entrée pour les tests
"SESSIONKEY1":
,
OriginalL'auteur
Vous pouvez faire votre RegEx un peu moche, mais
cela fonctionne
OriginalL'auteur