为什么java中的String.replaceAll()需要在正则表达式中使用4个斜杠“\\\\”来实际替换“\”?

时间:2013-09-18 15:03:57

标签: java regex string escaping charsequence

我最近注意到,String.replaceAll(regex,replacement)在转义字符“\”(斜杠)

时非常奇怪。

例如,考虑有一个带有文件路径的字符串 - String text = "E:\\dummypath" 我们希望将"\\"替换为"/"

text.replace("\\","/")提供输出"E:/dummypath",而text.replaceAll("\\","/")提出异常java.util.regex.PatternSyntaxException

如果我们想要与replaceAll()实现相同的功能,我们需要将其编写为, text.replaceAll("\\\\","/")

一个值得注意的区别是replaceAll()的参数为reg-ex,而replace()的参数为字符序列!

text.replaceAll("\n","/")与其char序列等效text.replace("\n","/")

完全相同

深入挖掘: 当我们尝试其他一些输入时,可以观察到更奇怪的行为。

让我们分配text="Hello\nWorld\n"

现在, text.replaceAll("\n","/")text.replaceAll("\\n","/")text.replaceAll("\\\n","/")所有这三个都提供相同的输出Hello/World/

Java确实以最好的方式搞砸了reg-ex!没有其他语言似乎在reg-ex中具有这些有趣的行为。有什么特别的原因,为什么Java搞砸了?

6 个答案:

答案 0 :(得分:25)

你需要转义两次,一次是Java,一次是正则表达式。

Java代码

"\\\\"

生成正则表达式字符串

"\\" - two chars

但是正则表达式也需要一个转义,所以它变成了

\ - one symbol

答案 1 :(得分:22)

@Peter Lawrey的回答描述了这些机制。 “问题”是反斜杠是Java字符串文字和正则表达式的迷你语言中的转义字符。因此,当您使用字符串文字来表示正则表达式时,有两组转义需要考虑...取决于您希望正则表达式的含义。

但为什么会那样?

这是历史性的事情。 Java最初根本没有正则表达式。 Java字符串文字的语法规则是从C / C ++中借用的,它也没有内置的正则表达式支持。在Java 1.4中以Pattern类的形式添加正则表达式支持之前,双重转义的重要性在Java中并不明显。

那么其他语言如何设法避免这种情况呢?

他们通过为编程语言本身的正则表达式提供直接或间接的语法支持来实现它。例如,在Perl,Ruby,Javascript和许多其他语言中,有一种模式/正则表达式的语法(例如'/ pattern /'),其中字符串文字转义规则不适用。在C#和Python中,它们提供了另一种“原始”字符串文字语法,其中反斜杠不会转义。 (但请注意,如果使用普通的C#/ Python字符串语法,则存在双重转义的Java问题。)


  

为什么text.replaceAll("\n","/")text.replaceAll("\\n","/")text.replaceAll("\\\n","/")都会提供相同的输出?

第一种情况是字符串级别的换行符。 Java正则表达式语言将所有非特殊字符视为自己匹配。

第二种情况是反斜杠,后跟字符串级别的“n”。 Java正则表达式语言解释反斜杠后跟“n”作为换行符。

最后一种情况是反斜杠,后跟字符串级别的换行符。 Java正则表达式语言不会将其识别为特定(正则表达式)转义序列。但是在正则表达式语言中,反斜杠后跟任何非字母字符意味着后一个字符。因此,反斜杠后跟换行符......意味着与换行符相同。

答案 2 :(得分:4)

1)假设您要使用Java的\方法替换单个replaceAll

\
˪--- 1) the final backslash

2)Java的replaceAll方法将正则表达式作为第一个参数。在正则表达式文字中,\具有特殊含义,例如在\d中,[0-9](任意数字)的快捷方式。在正则表达式文字中转义元数据的方法是在它之前加上\,这会导致:

\\
|˪--- 1) the final backslash
˪---- 2) the backslash needed to escape 1) in a regex literal

3)在Java中,没有正则表达式文字:你在字符串文字中编写正则表达式(例如,与JavaScript不同,你可以在其中编写{{1} })。但在字符串文字中,/\d+/也有特殊含义,例如:在\(新行)或\n(标签)中。在字符串文字中转义元数据的方法是在它之前加上\t,这导致:

\

答案 3 :(得分:-1)

这是因为Java试图在替换字符串中赋予\特殊含义,因此\ $将是一个文字$符号,但在这个过程中它们似乎已经删除了{{{{{ 1}}

虽然\,至少可以被认为在某种意义上是可以的(尽管它本身并不是绝对正确的),所有三个执行,text.replaceAll("\\\\","/")text.replaceAll("\n","/"),{{ 1}}给出相同的输出似乎更有趣。由于同样的原因,他们为什么限制了text.replaceAll("\\n","/")的功能,这恰恰是矛盾的。

Java没有搞乱正则表达式。这是因为,Java完全不需要时,试图通过尝试做一些独特而不同的事情来搞乱程序员。

答案 4 :(得分:-2)

解决此问题的一种方法是将反斜杠替换为另一个字符,使用该替代字符进行中间替换,然后在最后将其转换回反斜杠。例如,要将“\ r \ n”转换为“\ n”:

String out = in.replace('\\','@').replaceAll("@r@n","@n").replace('@','\\');

当然,如果您选择输入字符串中可能出现的替换字符,那将无法正常工作。

答案 5 :(得分:-3)

我认为java在String.replaceAll();

中真正搞乱了正则表达式

除了java之外,我从未见过这样一种语言解析正则表达式。如果你在其他一些语言中使用正则表达式,你会感到困惑。

如果在替换字符串中使用"\\",您可以使用java.util.regex.Matcher.quoteReplacement(String)

String.replaceAll("/", Matcher.quoteReplacement("\\"));

通过使用此Matcher课程,您可以获得预期的结果。