29

我最近注意到,String.replaceAll(regex,replacement) 在涉及转义字符“\”(斜杠)时表现得非常奇怪

例如,考虑有一个带有文件路径的字符串 -String text = "E:\\dummypath" 我们想"\\""/".

text.replace("\\","/")给出输出"E:/dummypath"text.replaceAll("\\","/")引发异常java.util.regex.PatternSyntaxException

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

一个显着的区别是replaceAll()它的参数是正则表达式,而replace()参数是字符序列!

text.replaceAll("\n","/")与它的 char-sequence 等价物完全相同text.replace("\n","/")

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

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

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

Java 真的以我觉得最好的方式搞砸了 reg-ex!似乎没有其他语言在 reg-ex 中有这些有趣的行为。任何具体原因,为什么Java会这样搞砸?

4

6 回答 6

28

您需要转义两次,一次用于 Java,一次用于正则表达式。

Java 代码是

"\\\\"

制作一个正则表达式字符串

"\\" - two chars

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

\ - one symbol
于 2013-09-18T15:06:34.607 回答
25

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

但为什么会这样呢?

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

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

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


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

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

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

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

于 2013-09-18T17:06:25.293 回答
7

1)假设你想\用Java的replaceAll方法替换一个:

   \
   ˪--- 1) the final backslash

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

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

3)在Java中,没有正则表达式文字:您在字符串文字中编写正则表达式(例如,与JavaScript不同,您可以在其中编写/\d+/)。但在字符串字面量中,\也有特殊含义,例如在\n(新行)或\t(制表符)中。在字符串文字中转义 metachar 的方法是在它前面加上 a \,这会导致:

\\\\
|||˪--- 1) the final backslash
||˪---- 3) the backslash needed to escape 1) in a string literal
|˪----- 2) the backslash needed to escape 1) in a regex literal
˪------ 3) the backslash needed to escape 2) in a string literal
于 2016-05-19T17:05:11.280 回答
-2

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

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

Java 没有搞砸正则表达式。这是因为,Java 喜欢在根本不需要的情况下尝试做一些独特和不同的事情来搞乱编码人员。

于 2013-09-18T17:09:46.523 回答
-2

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

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

当然,如果您选择输入字符串中可能出现的替换字符,那将不会很好地工作。

于 2015-07-29T18:12:49.283 回答
-4

我认为 java 在 String.replaceAll(); 中真的搞砸了正则表达式;

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

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

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

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

于 2013-09-18T15:15:01.313 回答