Подтвердить что ты не робот

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

Почему я получаю это? И какое правильное исправление? Спасибо заранее!

4b9b3361

Ответ 1

В ${fizz}

{ является индикатором для механизма регулярных выражений, который вы собираетесь запустить индикатор повторения, например {2,4}, что означает "от 2 до 4 раз от предыдущего токена". Но {f является незаконным, поскольку за ним должно следовать число, поэтому оно генерирует исключение.

Вам нужно избежать всех метасимволов регулярных выражений (в этом случае $, { и }) (попробуйте использовать http://docs.oracle.com/javase/6/docs/api/java/util/regex/Pattern.html#quote(java.lang.String)) или используйте другой метод, который заменяет строку для строки, а не регулярное выражение для строки.

Ответ 2

Как указывает Паташу, проблема заключается в 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;
}

Ответ 3

Адаптировано из 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();
}

Ответ 4

Используйте String-replaceAll. Пример ввода Строка для тестирования "SESSIONKEY1":

"$ {SOMESTRING.properties.SESSIONKEY1}"

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

Ответ 5

Вы можете сделать свой RegEx немного уродливым, но это будет работать

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