java escape-круглые скобки

у меня есть этот маленький класс для многократной замены строки:

import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang.StringUtils;
public class MultipleReplace {
    public static void main(String[] args) {
        Map<String,String> tokens = new HashMap<String,String>();
        tokens.put(":asd:", "<img src=asd.gif>");
        tokens.put(":)", "<img src=sorriso.gif>");
        String template = ":asd: bravo! :)";
        String patternString = "(" + StringUtils.join(tokens.keySet(), "|") + ")";
        Pattern pattern = Pattern.compile(patternString);
        Matcher matcher = pattern.matcher(template);
        StringBuffer sb = new StringBuffer();
        while(matcher.find()) {
            matcher.appendReplacement(sb, tokens.get(matcher.group(1)));
        }
        matcher.appendTail(sb);

        System.out.println(sb.toString());
    }
}

Проблема заключается во второй замене, где у меня есть скобка, которая приводит к:

Исключение в потоке "main" java.util.regex.PatternSyntaxException: несопоставленное закрывающее ')' рядом с индексом 8 (:)|:asd:)

Как я могу избежать скобок? Или вы можете предложить альтернативу для этой многократной замены?

Большое спасибо и извините за мой английский :)

ИЗМЕНИТЬ:

Экранирование с помощью обратной косой черты ')' тоже не работает, оно не скомпилируется:

"Недопустимая управляющая последовательность (допустимы \b \t \n \f \r \" \' \ )"

НОВОЕ ИЗМЕНЕНИЕ

с использованием двух обратных косых черт компилируется, но не выполняет замену.

ПОСЛЕДНЕЕ ИЗМЕНЕНИЕ

Наконец нашел решение, используя Pattern.quote при построении шаблона. Придется использовать итератор для выполнения цикла.

Вот правильный код:

package string;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class MultipleReplace {
    @SuppressWarnings("rawtypes")
    public static void main(String[] args) {
        Map<String,String> tokens = new HashMap<String,String>();
        tokens.put(":asd:", "<img src=asd.gif>");
        tokens.put(":)", "<img src=sorriso.gif>");
        String template = ":asd: bravo! :)";
        Iterator it = tokens.entrySet().iterator();
        String patternString = "(";
        while (it.hasNext()) {
            Map.Entry pairs = (Map.Entry)it.next();
            System.out.println(pairs.getKey() + " = " + pairs.getValue());
            patternString = patternString +Pattern.quote((String) pairs.getKey());
            if (it.hasNext())
            {
                patternString = patternString + "|";
            }
        }
        patternString = patternString + ")";
        System.out.println(patternString);
        Pattern pattern = Pattern.compile(patternString);
        Matcher matcher = pattern.matcher(template);
        StringBuffer sb = new StringBuffer();
        while(matcher.find()) {
            matcher.appendReplacement(sb, tokens.get(matcher.group(1)));
        }
        matcher.appendTail(sb);
        System.out.println(sb.toString());
    }
}

Пожалуйста, прокомментируйте это, если я могу улучшить работу! Большое спасибо!


person Laphroaig    schedule 15.02.2011    source источник
comment
Ваша замена в целом в порядке, но вы можете столкнуться с проблемами, когда ваши подлежащие замене токены не исправлены. Рассмотрите возможность использования tokens.put(Pattern.quote(original), replacement). Кстати, вам не нужна группа, используйте matcher.group(0).   -  person maaartinus    schedule 16.02.2011
comment
Когда вы используете Pattern.quote, вам не нужно и не следует избегать скобок. Так что это еще одно решение.   -  person maaartinus    schedule 16.02.2011
comment
Как я могу сделать это с помощью нескомпилированного регулярного выражения (String.matching())?   -  person Vincent    schedule 26.03.2012


Ответы (3)


Используйте Pattern.quote, как я написал в комментарии. Он работает с каждой строкой, а для длинных строк, содержащих много не буквенно-цифровых символов, он менее подвержен ошибкам.

Обновлять

Это блестящее (и непроверенное) решение Java 8:

    final Map<String, String> tokens = new HashMap<>();
    tokens.put(":asd:", "<img src=asd.gif>");
    tokens.put(":)", "<img src=sorriso.gif>");
    final String template = ":asd: bravo! :)";

    final String patternString = tokens.keySet()
        .stream().map(Pattern::quote).collect(Collectors.joining("|"));
    final Pattern pattern = Pattern.compile(patternString);
    final Matcher matcher = pattern.matcher(template);
    final StringBuffer sb = new StringBuffer();
    while (matcher.find()) {
        matcher.appendReplacement(sb, tokens.get(matcher.group(0)));
    }
    matcher.appendTail(sb);
    System.out.println(sb.toString());
person maaartinus    schedule 15.02.2011
comment
спасибо, не могли бы вы привести пример: я пытаюсь сделать это: tokens.put(Pattern.quote(:)), ‹img src=/faccine/sorrisone.gif›); Шаблон строки = :asd: браво! :); - person Laphroaig; 16.02.2011
comment
но у меня есть исключение NullPointerException - person Laphroaig; 16.02.2011
comment
ПРОСТИ. Вы должны поместить исходную строку в карту, чтобы найти совпадение. Используйте строку в кавычках только в шаблоне. - person maaartinus; 16.02.2011
comment
Это относится к каждому решению. Независимо от того, как вы создаете шаблон для буквального сопоставления строки, вы должны поместить исходную строку в качестве ключа в карту. - person maaartinus; 16.02.2011
comment
хорошо, теперь, если я использую Pattern.compile(patternString, Pattern.LITERAL); без экранирования, у меня нет ошибок, но он не делает замены... Большое спасибо за вашу помощь @maartinus! :) - person Laphroaig; 16.02.2011
comment
О... ИЗВИНИТЕ еще раз... вы не можете использовать мое лучшее решение... даже |, соединяющий строки, цитируется! - person maaartinus; 16.02.2011
comment
Я лучше пойду спать. Еще одна попытка: tokens.put(original, replacement) без кавычек. Используйте цитирование только при построении шаблона. Это означает, что вы не можете использовать join и вместо этого вам нужен цикл. - person maaartinus; 16.02.2011
comment
да!! это работает :) Я только что обновил свой первый пост с правильным кодом. Большое спасибо! - person Laphroaig; 16.02.2011

Используйте обратную косую черту: \). Parens необходимо экранировать, потому что они могут использоваться для группировки частей регулярного выражения.

    String template = ":asd: bravo\\! :\\)";
person Tim    schedule 15.02.2011
comment
Это не работает, оно не скомпилируется: недопустимая управляющая последовательность (допустимые: \b \t \n \f \r \\' \\ ) - person Laphroaig; 16.02.2011
comment
@Laphroaig: Ах! Поставьте две обратные косые черты. - person Tim; 16.02.2011
comment
Используйте два из них! Две обратные косые черты в исходном коде Java означают одну обратную косую черту в строке, что вам и нужно. - person maaartinus; 16.02.2011
comment
с использованием двух обратных косых черт компилируется, но не выполняет замену. - person Laphroaig; 16.02.2011
comment
Это должно работать. Должно быть что-то еще не так. Забудьте об этом и переключитесь на Pattern.quote, если вы ищете точное совпадение. - person maaartinus; 16.02.2011

Я приведу лучший пример, чем Тим Н.

Допустим, у вас есть слово String.

word = "http://www.randomwebsite.com/images/That Image (English)";

Если вы хотите заменить скобки пробелами, просто выполните:

word.replaceAll("\\(", " ");

Вы должны сделать 2 двойных обратных слэша, чтобы заставить компилятор заткнуться и не ныть.

Также имейте в виду, что эта функция возвращает строку. Так что ты бы сделал

word = word.replaceAll("\\(", " ");

Если вы не хотите его видеть, просто распечатайте его.

person user819511    schedule 16.12.2012