Java转义圆括号

15
我有一个小类,可以对字符串进行多次替换:
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());
    }
}

问题在于第二个替换操作中,有一个括号导致了以下异常:

“Exception in thread "main" java.util.regex.PatternSyntaxException: Unmatched closing ')' near index 8 (:)|:asd:)”

我该如何转义括号呢?或者,你能否建议一种替代方案来进行多重替换?非常感谢您,对我的英语表示歉意 :)

编辑:

用反斜杠“\”转义括号也行不通,它无法编译:

“Invalid escape sequence (valid ones are \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());
    }
}

如果我可以改进工作,请您发表评论!非常感谢!


你的替换一般来说是正确的,但当你要替换的标记不固定时,可能会遇到问题。考虑使用 tokens.put(Pattern.quote(original), replacement)。顺便说一下,你不需要分组,可以使用 matcher.group(0) - maaartinus
当您使用Pattern.quote时,无需也不应该转义括号。因此,这是另一种解决方案。 - maaartinus
我怎样才能在未编译的正则表达式(String.matching())中实现这个? - Vincent
3个回答

16

像我在评论中写的那样使用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());

谢谢,你能提供一个例子吗?我正在尝试这个:tokens.put(Pattern.quote(":)"), "<img src=/faccine/sorrisone.gif>"); String template = ":asd: bravo! :)"; - Laphroaig
但是我遇到了一个空指针异常。 - Laphroaig
抱歉,您必须将原始字符串放入映射中才能找到匹配项。只在模式中使用带引号的字符串。 - maaartinus
这适用于每个解决方案。无论您如何创建匹配字符串的模式,都必须将原始字符串作为Map中的键。 - maaartinus
哦... 再次抱歉... 你不能使用我的“最佳”解决方案... 即使是用 | 连接字符串也会被引用! - maaartinus
显示剩余3条评论

10

使用反斜杠:\)。必须转义括号,因为它们可以用于分组正则表达式的部分。

    String template = ":asd: bravo\\! :\\)";

1
这个不行,它无法编译:"无效的转义序列(有效的包括\b、\t、\n、\f、\r、"、'、\)" - Laphroaig
3
@Laphroaig: 啊!请加上两个反斜杠。 - Tim
2
使用两个!在Java源代码中使用两个反斜杠表示一个字符串中的一个反斜杠,这正是您所需要的。 - maaartinus
使用两个反斜杠编译,但不进行替换。 - Laphroaig
1
它必须工作。肯定还有其他问题。如果你正在寻找精确匹配,请忘记它并切换到Pattern.quote - maaartinus

1

我会给出比Tim N.更好的例子。

假设你有一个字符串word。

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

如果您想将括号替换为空格,只需执行以下操作:
word.replaceAll("\\(", " ");

你需要输入两个双反斜杠才能让编译器停止报错。

同时请记住,该函数返回一个字符串。因此,你需要这样做:

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

除非你想看它,否则就把它打印出来。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接