Ruby正则表达式中的反斜杠+捕获组

如何在捕获的组之前跳过反斜杠?

例:

"foo+bar".gsub(/(\+)/, '\\\1') 

我期望(和想要):

 foo\+bar 

我不幸得到的:

 foo\\1bar 

我该怎么逃到这里?

正如其他人所说,你需要两次逃脱该字符串中的所有内容。 所以在你的情况下,解决方案是使用'\\\\\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不是单引号中的有效转义序列,而是按字面处理。

附录

这个问题通常被隐藏的原因之一是由于使用了/.+/ style regexp引号,Ruby以一种特殊的方式处理,以避免双重转义所有内容。 (当然,这不适用于gsub替换字符串。)但是如果在Regexp.new使用字符串文字而不是regexp文字,您仍然可以看到它的Regexp.new

 Regexp.new("\.").match("a") #=> # Regexp.new("\\.").match("a") #=> nil 

正如你所看到的,我们不得不双重逃脱. 因为它被理解为文字. 由regexp引擎,因为".""\." 两者都评价为. 在双引号字符串中,但我们需要引擎本身接收\.

这是由于双字符串转义而发生的。 在这种情况下,您应该使用5个斜杠。

 "foo+bar".gsub(/([+])/, '\\\\\1') 

再添加\两次正确地逃脱了这一点。

 irb(main):011:0> puts "foo+bar".gsub(/(\+)/, '\\\\\1') foo\+bar => nil