wake-up-neo.net

Java PatternSyntaxException: Illegale Wiederholung der Zeichenkettenersetzung?

Ich versuche, eine Methode zu schreiben, die eine String akzeptiert, sie auf Instanzen bestimmter Token untersucht (z. B. ${fizz}, ${buzz}, ${foo} usw.) und jedes Token durch einen neuen String ersetzt, der aus einem Map<String,String> abgerufen wird.

Wenn ich beispielsweise diese Methode übergebe, muss die folgende Zeichenfolge eingegeben werden:

"Wie jetzt $ {fizz} Kuh. Der $ {buzz} hatte ungewöhnlich geformte $ {foo}."

Und wenn die Methode den folgenden Map<String,String> verwendet hat:

Key             Value
==========================
"fizz"          "brown"
"buzz"          "arsonist"
"foo"           "feet"

Die resultierende Zeichenfolge wäre dann:

"Wie jetzt braune Kuh. Der Brandstifter hatte seltsam geformte Füße."

Hier ist meine Methode:

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

Wenn ich das ausführen, erhalte ich die folgende Ausnahme:

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

Warum bekomme ich das? Und was ist die richtige Lösung? Danke im Voraus!

20
user1768830

In ${fizz}

{ ist ein Indikator für die Regex-Engine, dass Sie eine Wiederholungsanzeige wie {2,4} starten werden, was "2 bis 4 Mal des vorherigen Tokens" bedeutet. Aber {f ist illegal, da eine Zahl gefolgt werden muss, sodass eine Ausnahme ausgelöst wird.

Sie müssen alle regulären Metazeichen (in diesem Fall $, { und }) maskieren (versuchen Sie es mit http://docs.Oracle.com/javase/6/). docs/api/Java/util/regex/Pattern.html # quote (Java.lang.String) ) oder verwenden Sie eine andere Methode, die einen String durch einen String ersetzt, keinen regulären Ausdruck für einen String.

37
Patashu

Wie von Patashu ausgeführt, liegt das Problem in replaceFirst(token, replacementValue), das im ersten Argument einen Regex erwartet, kein Literal. Ändern Sie es in replaceFirst(Pattern.quote(token), replacementValue) und Sie werden es gut machen.

Ich habe auch die erste Regex etwas geändert, da es schneller geht mit + anstelle von *, aber das ist nicht notwendig.

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

Angepasst von 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

Sie können Ihre RegEx etwas hässlich machen, aber Das wird funktionieren

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

Verwenden Sie String-replaceAll . Beispieleingabezeichenfolge zum Testen von "SESSIONKEY1":

"$ {SOMESTRING.properties.SESSIONKEY1}"

.

    String pattern = "\\\"\\$\\{SOMESTRING\\.[^\\}]+\\}\\\""; 
    System.out.println(pattern);
    String result = inputString.replaceAll(pattern, "null");
    return result.toString();
0
Milan Das