正如其他人所说,您需要将该字符串中的所有内容转义两次。所以在你的情况下,解决方案是使用'\\\\\1'
or '\\\\\\1'
。但既然你问了为什么,我会尽力解释那部分。
原因是替换序列被解析两次——一次由 Ruby 解析,一次由底层正则表达式引擎解析,对于谁来说\1
是它自己的转义序列。 (使用双引号字符串可能更容易理解,因为单引号会产生歧义,其中'\\1'
and '\1'
是等价的但是'\'
and '\\'
不是。)
例如,这里用捕获的组和双引号字符串进行简单替换将是:
"foo+bar".gsub(/(\+)/, "\\1") #=> "foo+bar"
这传递了字符串\1
到正则表达式引擎,它将其理解为对捕获组的引用。在 Ruby 字符串文字中,"\1"
完全意味着其他东西(ASCII 字符 1)。
在这种情况下,我们真正想要的是正则表达式引擎接收\\\1
。它也明白\
作为转义字符,所以\\1
是不够的,只会评估文字输出\1
。所以,我们需要\\\1
在正则表达式引擎中,但要达到这一点,我们还需要使其通过 Ruby 的字符串文字解析器。
为此,我们采用所需的正则表达式输入,并再次将每个反斜杠加倍以通过 Ruby 的字符串文字解析器。\\\1
因此需要"\\\\\\1"
。在单引号的情况下,可以省略一个斜杠,如下所示\1
不是单引号中的有效转义序列,并且按字面意思处理。
Addendum
由于使用了该问题通常被隐藏的原因之一/.+/
style regexpquotes,Ruby 以一种特殊的方式处理它,以避免需要双重转义所有内容。 (当然,这不适用于gsub
替换字符串。)但是如果您在中使用字符串文字而不是正则表达式文字,您仍然可以看到它的实际效果Regexp.new
:
Regexp.new("\.").match("a") #=> #<MatchData "a">
Regexp.new("\\.").match("a") #=> nil
正如你所看到的,我们必须双重转义.
以便将其理解为字面意思.
通过正则表达式引擎,因为"."
and "\."
两者均评估为.
在双引号字符串中,但我们需要引擎本身来接收\.
.