为什么 Java 中的 String.replaceAll() 需要正则表达式中的 4 个斜杠 “\\\\” 才能实际替换 “\”?

2022-09-01 12:55:06

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

例如,假设有一个带有 filepath 的字符串 - 我们想要用 .String text = "E:\\dummypath""\\""/"

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

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

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

但工作原理与其字符序列等效项完全相同text.replaceAll("\n","/")text.replace("\n","/")

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

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

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

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


答案 1

您需要执行两次操作,一次用于 Java,一次用于正则表达式。

Java 代码是

"\\\\"

使正则表达式字符串

"\\" - two chars

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

\ - one symbol

答案 2

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

但为什么会这样呢?

这是一件历史性的事情。Java最初根本没有正则表达式。Java String文本的语法规则是从C / C++借来的,C / C++也没有内置的正则表达式支持。双重转义的尴尬在Java中并没有变得明显,直到他们以类的形式添加了正则表达式支持......在 Java 1.4 中。Pattern

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

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


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

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

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

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