如何在捕获的组之前触发反斜杠?
示例:
"foo+bar".gsub(/(\+)/, '\\\1')
我期望(和想要):
foo\+bar
我不幸得到了:
foo\\1bar
我如何正确逃离?
答案 0 :(得分:7)
正如其他人所说,你需要两次逃脱该字符串中的所有内容。因此,在您的情况下,解决方案是使用'\\\\\1'
或'\\\\\\1'
。但既然你问为什么,我会试着解释那个部分。
原因是替换序列被解析两次 - 一次是Ruby,一次是底层正则表达式引擎,\1
是它自己的转义序列。 (使用双引号字符串可能更容易理解,因为单引号引入了歧义,其中'\\1'
和'\1'
是等价的,但'\'
和'\\'
不是。)
因此,例如,此处使用捕获的组和双引号字符串进行简单替换将是:
"foo+bar".gsub(/(\+)/, "\\1") #=> "foo+bar"
这将字符串\1
传递给regexp引擎,它将其理解为对捕获组的引用。在Ruby字符串文字中,"\1"
完全意味着其他东西(ASCII字符1)。
在这种情况下我们真正想要的是regexp引擎接收\\\1
。它还将\
理解为转义字符,因此\\1
是不够的,只会评估为文字输出\1
。因此,我们在regexp引擎中需要\\\1
,但要达到这一点,我们还需要使它超过Ruby的字符串文字解析器。
为此,我们采用所需的正则表达式输入并再次加倍每个反斜杠以通过Ruby的字符串文字解析器。因此\\\1
需要"\\\\\\1"
。在单引号的情况下,可以省略一个斜杠,因为\1
不是单引号中的有效转义序列,而是按字面处理。
这个问题通常被隐藏的原因之一是由于使用了/.+/
样式的正则表达式引用,Ruby以一种特殊的方式处理,以避免双重转义所有内容。 (当然,这不适用于gsub
替换字符串。)但如果在Regexp.new
中使用字符串文字而不是正则表达式字面值,您仍然可以看到它:
Regexp.new("\.").match("a") #=> #<MatchData "a">
Regexp.new("\\.").match("a") #=> nil
正如您所看到的那样,我们不得不双重转义.
,因为.
和"."
所以regexp引擎将其理解为文字"\."
两者都在双引号字符串中评估为.
,但我们需要引擎本身才能接收\.
。
答案 1 :(得分:4)
这是由于双字符串转义而发生的。在这种情况下你应该使用5个斜杠。
"foo+bar".gsub(/([+])/, '\\\\\1')
答案 2 :(得分:2)
再添加\
两次,可以正确地解决这个问题。
irb(main):011:0> puts "foo+bar".gsub(/(\+)/, '\\\\\1')
foo\+bar
=> nil