为什么java中的String.replaceAll()需要4个斜杠\\\\&引用;在正则表达式中,实际替换\&引用;?

为什么java中的String.replaceAll()需要4个斜杠\\\\&引用;在正则表达式中,实际替换\&引用;?,java,regex,string,escaping,charsequence,Java,Regex,String,Escaping,Charsequence,我最近注意到,String.replaceAll(regex,replacement)在转义字符“\”(斜杠)上的行为非常奇怪 例如,考虑有一个带有FielpAth-字符串文本的字符串=“e:\DimyPATH”< /C> 我们想用“/”替换“\\\” text.replace(“\\”,“/”给出输出“E:/dummypath”,而text.replaceAll(“\\”,“/”)引发异常java.util.regex.PatternSyntaxException 如果我们想用replaceA

我最近注意到,String.replaceAll(regex,replacement)在转义字符“\”(斜杠)上的行为非常奇怪

例如,考虑有一个带有FielpAth-<代码>字符串文本的字符串=“e:\DimyPATH”< /C> 我们想用

“/”
替换
“\\\”

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

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

一个显著的区别是
replaceAll()
的参数为reg-ex,而
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会这样一团糟?

您需要esacpe两次,一次用于Java,一次用于regex

Java代码是

"\\\\"
生成一个正则表达式字符串

"\\" - two chars
但是正则表达式也需要一个逃避,所以它变成了

\ - one symbol

我认为java真的弄乱了String.replaceAll()中的正则表达式

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

如果在替换字符串中使用
“\\”
,则可以使用
java.util.regex.Matcher.quoteReplacement(字符串)


通过使用这个
Matcher
类,您可以得到预期的结果。

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

但为什么会这样呢

这是一个历史事件。Java最初根本没有正则表达式。java字符串文字的语法规则是从C/C++中借用的,它也没有内置的正则表达式支持。直到他们以
模式
类的形式添加了regex支持,双重转义的尴尬才在Java中显现出来。。。在Java1.4中

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

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


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

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

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


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

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

虽然
text.replaceAll(“\\\\”,“/”)
,至少在某种意义上可以被认为是正常的(尽管它本身不是绝对正确的),但所有三次执行,
text.replaceAll(“\n”,“/”
text.replaceAll(\\\n“,“/”)给出相同输出的操作似乎更有趣。出于同样的原因,他们限制了
text.replaceAll(“\\”,“/”)的功能,这与此正好相反


Java没有搞乱正则表达式。这是因为,Java喜欢在完全不需要反斜杠的情况下,通过尝试做一些独特和不同的事情来搞乱编码器。

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

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

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

\
——最后一个反斜杠
2) Java的
replaceAll
方法将正则表达式作为第一个参数。在正则表达式文本中,
\
具有特殊含义,例如在
\d
中,这是
String.replaceAll("/", Matcher.quoteReplacement("\\"));
String out = in.replace('\\','@').replaceAll("@r@n","@n").replace('@','\\');