使用String#sub在Ruby中用'\&'替换'&'

Chr*_*dig 2 ruby regex

我正在尝试使用Ruby 替换&字符串中的每个字符串.我所看到的让我困惑,因为我希望得到:\&String#gsubmilk \& honey

irb(main):009:0> puts "milk & honey".sub(/&/,'\ &')
milk \ & honey
=> nil
irb(main):010:0> puts "milk & honey".sub(/&/,'\&')
milk & honey
=> nil
irb(main):011:0> puts "milk & honey".sub(/&/,'\\&')
milk & honey
=> nil
irb(main):012:0> 
Run Code Online (Sandbox Code Playgroud)

这是在OS X上的Ruby 2.0.0p481上.(我在String#sub上面使用但计划String#gsub用于&字符串中多个的一般情况.)

Mar*_*eed 5

将字符串作为替换值传递给String#sub(或String#gsub)时,首先会扫描该字符串以反向引用原始字符串.这里特别感兴趣的\&是,序列被匹配整个正则表达式的字符串的任何部分所取代:

puts "bar".gsub(/./, '\\&\\&')   # => bbaarr
Run Code Online (Sandbox Code Playgroud)

请注意,尽管有外观,但Ruby字符串文字'\\&\\&'表示只包含四个字符的字符串,而不是六个字符:

puts '\\&\\&'  # => \&\&
Run Code Online (Sandbox Code Playgroud)

这是因为即使是单引号的Ruby字符串也会受到反斜杠替换,以便在单引号字符串中包含单引号.只有'或另一个反斜杠本身触发替代; 反斜杠后跟其他任何东西都只是一个字面反斜杠.这意味着您通常可以获得文字反斜杠而不会加倍:

    puts '\&\&'  # still => \&\&
Run Code Online (Sandbox Code Playgroud)

但这是一个依赖的细节,因为下一个角色可能会改变解释.最安全的做法是将所有反斜杠加倍,然后将字符串显示在字符串中.

现在,在这种情况下,我们要以某种方式得到一个反斜杠,符号退了出去sub.幸运的是,就像Ruby字符串解析器一样,sub允许我们使用加倍的反斜杠来指示反斜杠应该作为文字而不是作为反向引用的开头.我们只需要将sub接收到的字符串中的反斜杠加倍 - 这意味着将字符串的文字表示中的两个反斜杠加倍,使我们总共以这种形式的四个反斜杠:

puts "milk & honey".sub(/&/, '\\\\&')
Run Code Online (Sandbox Code Playgroud)

如果你喜欢危险的生活,你可以在这里只有三个反斜杠.:)

或者,您可以避免所有反斜杠计数并使用块形式,其中通过调用代码块而不是解析静态字符串来获取替换.由于块可以自由地进行任何类型的替换或字符串修改,因此它的返回值不会被扫描为反斜杠替换,例如字符串版本是:

puts "milk & honey".sub(/&/) { '\\&' }
Run Code Online (Sandbox Code Playgroud)

或"风险"版本:

puts "milk & honey".sub(/&/) { '\&' }
Run Code Online (Sandbox Code Playgroud)