Can someone explain this string substitution issue to me?

Using 1.9.3p194:

puts “+”

puts “\+”
+

puts “\”
\

However not what I’d expect (1):

puts “hi + bye”.gsub(/+/, “\+”)
hi bye

This gives the expected output:

puts “hi + bye”.gsub(/+/, “\\+”)
hi + bye

I can understand why I need to double escape the + to get + in the
output but shouldn’t (1) above at least print “hi + bye”?

[EDIT: Improved title]

Because an escape, e.g. \x in a replacement string is the syntax for a
backreference. After ruby evaluates your string to produce +, ruby
hands + to the
regex engine, and the regex engine sees + in a replacement string,
which is the syntax for referring to a matching
subgroup (backreference). In your case, + is especially tricky because
it is a backreference to the global variable $+, which is the last match
from a successful regex match, which in your case is nil. Observe:

puts “hi + bye”.gsub(/(.) +/, " plus \1")

–output:–
h plus i bye

puts $+

–output:–
i

puts “hi + bye”.gsub(/(.) +/, " plus \+")

–output:–
h plus i bye

the global variable $+, which is the last match
from a successful regex match, which in your case is nil.

That should read:

…the global variable $+, which is the last match
from a previous successful regex match, which in your case is nil.

7stud – wrote in post #1074118:

the regex engine sees + in a replacement string,
which is the syntax for referring to a matching
subgroup (backreference).

Many thanks 7stud - I thought there would have to be a reason behind it

  • that makes sense now.

I don’t see immediate answers to this in the ML but others might still
have answered it already.

On Fri, Aug 31, 2012 at 5:50 PM, Luke P. [email protected]
wrote:

I can understand why I need to double escape the + to get + in the
output but shouldn’t (1) above at least print “hi + bye”?

There are two levels of escapes and it happens that the backslash is
the escape character for both levels:

  1. string (i.e. what makes “\n” create a string with the line
    terminator)
  2. regular expression replacement pattern (e.g. \1 as a backreference)

If you want to have a literal backslash in the result, you need to
escape it, i.e. you need this in the replacement string
\

To get that, you need to escape each backslash so string escaping does
not eat it, so you do

“\\”

irb(main):005:0> puts “\”

=> nil
irb(main):006:0> puts “\\”
\
=> nil

Kind regards

robert