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!)
Почему я получаю это? И какое правильное исправление? Спасибо заранее!
Ответы
Ответ 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 = "\\$[\\{]([^}]*)[\\}]";