From: Robert Klemme Date: 2004-01-22T22:24:58+09:00 Subject: Re: gsub oddity? "Mike Stok" schrieb im Newsbeitrag news:MWOPb.54576$lGr.29805@twister01.bloor.is.net.cable.rogers.com... > In article , > Robert Klemme wrote: > > > >"Mark J. Reed" schrieb im Newsbeitrag > >news:20040121221951.GD16733@mulan.thereeds.org... > >> On Thu, Jan 22, 2004 at 06:57:22AM +0900, Ralph Mason wrote: > >> > What am I missing here. I am trying to replace a single quote with > >the > >> > string \' > >> > >> You're missing an extra level of processing applied to the replacement > >> string for gsub in order to let you include parts of what you're > >> replacing. > > > >No, in this case he's missing extra backslashes: > > > >irb(main):002:0> str='a\'o' > >=> "a'o" > >irb(main):003:0> str.gsub(/'/, '\\\\\'' ) > >=> "a\\'o" > >irb(main):004:0> puts str.gsub(/'/, '\\\\\'' ) > >a\'o > >=> nil > > > >It does look a bit odd, but the explanation is logical: There are two > >levels of escaping involved. The first level is that needed for the ruby > >parser to get the expected characters into the replacement string. The > >second level is used in order to be able to use a backslash literally in > >the substitution string. > > > >irb(main):005:0> puts '\\\\\'' > >\\' > >=> nil > > > >You need two backslashes in the substitution string to prevent gsub from > >interpreting the backslash as escape char. That makes four backslashes > >when entering the string. The fifth backslash escapes the single quote in > >order to be able to include it into the string. > > You can make life a little easier by picking a quote other than ' to > get rid of one backslash e.g. Yes, although it's just a small improvement. > [mike@ratdog mike]$ irb --simple-prompt > >> str = "a'o" > => "a'o" > >> puts str.gsub(/'/, "\\\\'") > a\'o > => nil > > It can be tiresome to remember how many times a string will be scanned > in a context where \ characters matter, so I prefer using the block form > e.g. > > >> puts str.gsub(/(')/) { |c| "\\#{c}" } > a\'o > => nil > > ...but I have never measured whether this is significantly more > expensive. It's definitely more expensive, which is not really a surprise given the additional overhead of - a block yield - string replacement with {} But even with a constant string there's still the block call overhead: 14:21:43 [ADMIN]: /c/temp/ruby/rx-replace-bm.rb user system total real direct 0.766000 0.000000 0.766000 ( 0.763000) direct & 0.766000 0.000000 0.766000 ( 0.769000) block 1.765000 0.000000 1.765000 ( 1.770000) block fix 1.438000 0.000000 1.438000 ( 1.432000) 14:21:50 [ADMIN]: cat /c/temp/ruby/rx-replace-bm.rb #!/usr/bin/ruby require 'benchmark' include Benchmark N=100000 str = ' /* comment */ String s = "***/"; ' bm(10) do |x| x.report("direct") do for n in 1..N str.gsub( /"/, '\\\\"' ) end end x.report("direct &") do for n in 1..N str.gsub( /"/, '\\\\\\&' ) end end x.report("block") do for n in 1..N str.gsub( /"/ ) {|m| "\\#{m}"} end end x.report("block fix") do for n in 1..N str.gsub( /"/ ) {|m| '\\"'} end end end 14:21:58 [ADMIN]: > Hal, is this a FAQ? "How do I replace ' with \' in a string?" I'm sure > that this has come up a few times... I put it on the Wiki: http://www.rubygarden.org/ruby?RegexpCookbook Cheers robert