Bug #3147

gsub evaluates the passed in replacement string making results unpredictable when replacement string is dynamically generated

Added by wwalker (Wayne Walker) over 9 years ago. Updated over 8 years ago.

ruby -v:
ruby 1.8.6 (2009-08-04 patchlevel 383) [i386-linux]


after the interpreter evaluates the replacement string (if it's a literal in "" or ''), gsub further evaluates it during execution changing the effective replacement string.

irb(main):016:0> a = 'Y'
=> "Y"
irb(main):017:0> b = 'Z\1'
=> "Z\1"
irb(main):018:0> c = '\\1'
=> "\\1"
irb(main):019:0> a.gsub(/(Y)/, b)
=> "ZY"
irb(main):020:0> a.gsub(/(Y)/, c)
=> "\1"

The last result is expected to be "\Y"



Updated by wwalker (Wayne Walker) over 9 years ago

This bug also exists in :

jruby 1.3.1 (ruby 1.8.6p287) (2009-06-15 2fd6c3d)
ruby 1.9.1p376 (2009-12-07 revision 26041) [i686-linux]


Updated by shyouhei (Shyouhei Urabe) over 9 years ago

  • Status changed from Open to Rejected

"\\1", backslash-backslash-one, is a backslash-escaped backslash plus literal 1.
If what you want to get "\Y" you need another backslash to escape 1 i.e. "\\\1",
backslash-backslash backslash-one.


Updated by wwalker (Wayne Walker) over 9 years ago

This at least needs to be documented clearly in gsub. Strings are normally only evaluated once, not twice.



Updated by murphy (Kornelius Kalnbach) over 9 years ago

I use the pattern str.gsub(pattern) { replacement } when I'm not sure
about the replacement. In this case:

'Y'.gsub(/(Y)/) { '\' + $1 }

This specific example could also be written as:

'Y'.gsub(/(?=Y)/, '\')

Both avoid the backslash issue.

Escaping rules in replacement strings are indeed confusing. If you want
a single backslash in the output, you need four of them in the literal.

Adding Wayne's examples to the documentation would certainly help to
understand this.



Also available in: Atom PDF