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