web-dev-qa-db-ja.com

Java PatternSyntaxException:文字列置換の不正な繰り返し?

私はStringを受け入れ、特定のトークン(たとえば、${fizz}${buzz}${foo}など)のインスタンスを検査して置き換えるメソッドを作成しようとしています。 Map<String,String>から取得される新しい文字列を持つ各トークン。

たとえば、このメソッドに次の文字列を渡すと:

「どうして今$ {fizz}牛。$ {buzz}は奇妙な形の$ {foo}でした。」

メソッドが次のMap<String,String>を参照した場合:

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

その場合、結果の文字列は次のようになります。

「今は茶色の牛。放火犯は奇妙な形の足を持っていた。」

私の方法は次のとおりです。

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

これを実行すると、次の例外が発生します。

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

なぜこれを取得していますか?そして正しい修正は何ですか?事前に感謝します!

25
user1768830

_${fizz}_で

_{_は、繰り返しインジケーターを開始しようとしている正規表現エンジンへのインジケーターです。たとえば、_{2,4}_は、「前のトークンの2〜4回」を意味します。ただし、_{f_は、数字の後に続く必要があるため不正です。したがって、例外がスローされます。

すべての正規表現メタキャラクターをエスケープする必要があります(この場合は_$_、_{_、および_}_)( http://docs.Oracle.com/javase/6/ docs/api/Java/util/regex/Pattern.html#quote(Java.lang.String) )または、文字列の正規表現ではなく、文字列を文字列に置き換える別のメソッドを使用します。

37
Patashu

Patashuが指摘したように、問題はreplaceFirst(token, replacementValue)にあり、リテラルではなく、最初の引数に正規表現が必要です。それをreplaceFirst(Pattern.quote(token), replacementValue)に変更すると、大丈夫です。

また、最初の正規表現も少し変更しました。+ の代わりに *しかし、それは必要ではありません。

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

RegExを少しくすることもできますが、これは機能します

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

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

String-replaceAllを使用します。 「SESSIONKEY1」をテストするためのサンプル入力文字列:

「$ {SOMESTRING.properties.SESSIONKEY1}」

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