web-dev-qa-db-fra.com

Java PatternSyntaxException: Répétition illégale sur la substitution de chaîne?

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!

20
user1768830

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.

31
Patashu

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;
}
5
Miguel

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();
}
1
johnchen902

Vous pouvez rendre votre RegEx un peu moche, mais Cela fonctionnera

String regex = "\\$[\\{]([^}]*)[\\}]";
0

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();
0
Milan Das