将String.replaceAll中的单个反斜杠替换为双反斜杠

137

我试图使用replaceAllString \something\转换为String \\something\\,但我一直收到各种错误。我原以为这是解决方案:

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

但是这会导致以下异常:

java.util.regex.PatternSyntaxException: Unexpected internal error near index 1
5个回答

232

String#replaceAll() 方法将参数解释为正则表达式。在 Stringregex 中,\ 都是转义字符。如果要在正则表达式中使用它,需要进行双重转义。

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

但是您不一定需要使用正则表达式,因为您想要进行逐个字符的精确替换,并且在此处不需要模式。 因此,String#replace() 应该就足够了:

string.replace("\\", "\\\\");
更新:根据评论,您似乎想在JavaScript上下文中使用该字符串。建议您最好使用StringEscapeUtils#escapeEcmaScript(),以覆盖更多字符。

实际上,它用于JavaScript AST,应该转换回源代码。你的解决方案可行。谢谢! - Frank Groeneveld
3
如果您仍然想要使用String#replaceAll(),您可以使用 Matcher#quoteReplacement() 引用替换字符串:theString.replaceAll("\\", Matcher.quoteReplacement("\\\\"));。请注意不要改变原始意思。 - phse
1
Matcher.quoteReplacement( ... ) 是一个很好的方式!请参考 Pshemo 的回答! - Hartmut Pfarr

21

TLDR:请使用 theString = theString.replace("\\", "\\\\");


问题

replaceAll(target, replacement) 方法在处理 target 和部分 replacement 参数时使用正则表达式(regex)语法。

问题在于,在正则表达式中和字符串文字中,\ 都是特殊字符(例如,可以使用 \d 表示数字,在字符串文字中可以使用 "\n" 表示行分隔符,或者 \" 来转义双引号符号,否则会表示字符串文字的结尾)。

在这两种情况下,我们可以通过在其前面放置额外的 \ 来将其 转义(使其成为特定意义之外的字面值),从而创建 \ 符号(例如,我们可以通过 \" 转义字符串文字中的 ")。

因此,对于表示 \ 符号的 target 正则表达式,需要使用 \\,表示此文本的字符串字面值需要看起来像 "\\\\"

所以我们将 \ 转义了两次:

  • 一次在正则表达式中的 \\
  • 一次在字符串文字中的 "\\\\"(每个 \ 都用 "\\" 表示)。

对于 replacement 参数,\ 也是特殊字符。它允许我们转义其他特殊字符 $,通过 $x 表示法,使用被正则表达式匹配并由索引为 x 的捕获组持有的部分数据,例如:"012".replaceAll("(\\d)", "$1$1") 将匹配每个数字,将其放置在捕获组 1 中,并使用 $1$1 将其替换为两个副本(即复制它),结果为 "001122"

因此,为让 replacement 表示 \ 字面值,我们需要使用附加的 \ 进行转义,这意味着:

  • replacement 必须包含两个反斜杠字符 \\
  • 表示 \\ 的字符串字面值如 "\\\\"

但是,由于我们希望 replacement 拥有 两个 反斜杠,因此我们需要使用 "\\\\\\\\"(每个 \ 都用一个 "\\\\" 表示)。

因此,使用 replaceAll 的版本可以如下所示:

replaceAll("\\\\", "\\\\\\\\");

使用replaceAll更简单

为了使我们的生活更加轻松,Java提供了工具自动将文本转义为目标替换部分。因此,现在我们可以只关注字符串,而不必再考虑正则表达式的语法:

replaceAll(Pattern.quote(target), Matcher.quoteReplacement(replacement))

在我们的情况下可能看起来像

replaceAll(Pattern.quote("\\"), Matcher.quoteReplacement("\\\\"))

更好的方法:使用replace

如果我们不需要正则表达式语法支持,那就完全不用涉及replaceAll。相反,我们可以使用replace。两种方法都会替换所有target,但replace不涉及正则表达式语法。所以你只需简单地写成:

theString = theString.replace("\\", "\\\\");

14
为了避免此类问题,您可以使用replace(需要一个普通字符串)而不是replaceAll(需要一个正则表达式)。您仍然需要转义反斜杠,但不需要像正则表达式那样进行复杂的转义。

8

您需要转义第一个参数中的(转义)反斜杠,因为它是一个正则表达式。替换(第二个参数-参见Matcher#replaceAll(String))也有其特殊含义的反斜杠,所以您需要将其替换为:

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

3

是的...当正则表达式编译器看到您给出的模式时,它只看到一个反斜杠(因为Java的词法分析器将双反斜杠转换为单个反斜杠)。你需要用"\\\\"替换"\\\\",信不信由你! Java真的需要一个好的原始字符串语法。


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