J'essaie d'écrire une méthode qui acceptera une String
, l'inspecter pour détecter certains jetons (par exemple ${fizz}
, ${buzz}
, ${foo}
, etc.) et remplacer chaque jeton par une nouvelle chaîne extraite d'un Map<String,String>
.
Par exemple, si je transmets cette méthode à la chaîne suivante:
"Comment maintenant $ {fizz} vache. Le $ {buzz} avait une forme étrange de $ {foo}."
Et si la méthode a consulté le Map<String,String>
suivant:
Key Value
==========================
"fizz" "brown"
"buzz" "arsonist"
"foo" "feet"
La chaîne résultante serait alors:
"Comment maintenant vache brune. Le pyromane avait des pieds aux formes étranges."
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;
}
Quand je lance ceci, 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 je reçois ça? Et quelle est la solution correcte? Merci d'avance!
Dans ${fizz}
{
indique au moteur des expressions rationnelles que vous êtes sur le point de démarrer un indicateur de répétition, tel que {2,4}
, qui signifie "2 à 4 fois le jeton précédent". Cependant, {f
est illégal, car il doit être suivi d'un nombre, il génère donc une exception.
Vous devez éviter toutes les métacaractères regex (dans ce cas, $
, {
et }
) (essayez d’utiliser http://docs.Oracle.com/javase/6/docs/api/Java/util/regex/Pattern.html quote (Java.lang.String) ) ou utilisez une méthode différente qui substitue une chaîne à une chaîne et non une regex à une chaîne.
Comme le souligne Patashu, le problème réside dans replaceFirst(token, replacementValue)
, qui attend une expression régulière dans le premier argument, pas un littéral. Changez-le en replaceFirst(Pattern.quote(token), replacementValue)
et tout ira bien.
J'ai aussi légèrement changé la première expression rationnelle, car cela va plus vite avec +
au lieu de *
mais ce n'est pas nécessaire.
static String substituteAllTokens(Map<String,String> tokensMap, String toInspect) {
String regex = "\\$\\{([^}]+)\\}";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(toInspect);
String result = 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.");
result = result.replaceFirst(Pattern.quote(token), replacementValue);
}
return result;
}
Adapté de Matcher.replaceAll
boolean result = matcher.find();
if (result) {
StringBuffer sb = new StringBuffer();
do {
String tokenKey = matcher.group(1); // Ex: fizz
String replacement = Matcher.quoteReplacement(tokensMap.get(tokenKey));
matcher.appendReplacement(sb, replacement);
result = matcher.find();
} while (result);
matcher.appendTail(sb);
return sb.toString();
}
Vous pouvez rendre votre RegEx un peu moche, mais Cela fonctionnera
String regex = "\\$[\\{]([^}]*)[\\}]";
Utilisez String-replaceAll . Sample String d'entrée pour tester "SESSIONKEY1":
"$ {SOMESTRING.properties.SESSIONKEY1}"
,
String pattern = "\\\"\\$\\{SOMESTRING\\.[^\\}]+\\}\\\"";
System.out.println(pattern);
String result = inputString.replaceAll(pattern, "null");
return result.toString();