Ruby 巨坑爹的 String#gsub 方法

匿名 · 2013年07月19日 · 最后由 blackanger 回复于 2013年08月05日 · 11252 次阅读

Ruby 中怎样做到 String replace all substring? gsub?

# 注意我这里的' \" '不是为了转义双引号,而就是为了表达\和"两个字符
'site=|http://jex.im/|'.gsub '|','\"'     # ok ? 
# =>  "site=\\\"http://jex.im/\\\"" 

想当然地这样写不小心就踩中地雷了 看下面的代码结果:

# 注意我这里的' \& '不是为了转义&,而就是为了表达\和&两个字符
'site=|http://jex.im/|'.gsub '|','\"'  
# => "site=|quot;http://jex.im/|quot;" 

你竟然需要这么写才能得到正确结果:TOT

'site=|http://jex.im/|'.gsub('|') { '\"' }  
# => "site=\\"http://jex.im/\\""

坑爹啊!!!!!!!!!!!!!!!!!! 想想假如你的 replacement string 是从外部获得的,你不知道里面有'\1,&'这样的 back reference,结果会多么令人崩溃啊!!!! (注意上面的'\1,&'显示不正确,"&"前面的"\"被吃掉了,Ruby China 的 BUG 么?)

括弧,其实我的另类解决方案是这样的:

s.split(substr).join(replacement)

http://jex.im

同样,gsub! 和 sub 以及 sub! 方法都有这样的坑。只有 [] 方法不会这样

s["abc"]=replacement

Ruby 单引号是不会转义的!

2.0.0-p195 :001 > "site=|http://jex.im/|".gsub("|",'"')
 => "site=\"http://jex.im/\""

2.0.0-p195 :007 > 'site=|http://jex.im/|'.gsub '|',"\""
 => "site=\"http://jex.im/\""

2.0.0-p195 :008 > 'site=|http://jex.im/|'.gsub '|',"\""
 => "site="http://jex.im/""
匿名 #2 2013年07月19日

#1 楼 @huacnlee 你没明白,我这是特地构造出'\"'这样的示例代码的,不然怎么会出现'\"' 这样的串?

#2 楼 @jamchange 额,确实无法搞出来

1.9.3p429 :045 > '\&'
 => "\\&"

1.9.3p429 :026 > 'asd'.gsub('d', '<\&>')
 => "as<d>"

1.9.3p429 :047 > 'asd'.gsub('d', "<\\&>")
 => "as<d>"


你不是要去转义 &,而是应该去转义 \ 啊。因为 literal 被 ruby 读取的时候会做一次转义,所以你需要用 4 斜杠。不过单引号比较特殊,因为只有单引号和斜杠会转义,如果有斜杠后面不是这两种符号,会当成正常的一个斜杠。所以如果后面跟着其它字符单引号里用 3 斜杠也行。不过为了清楚,最好也用 4 个。

> 'abc'.gsub(/.+/, '\\\\&')
=> "\\&"

用户输入的话,因为不需要使用 literal string,把所有斜杠 double 一下就好了。

> r = 'a\\\''
=> "a\\'"
> 'abc'.gsub(/.+/, r.gsub('\\', '\\\\\\\\'))
=> "a\\'"

注意,你需要把一个斜杠替换成两个,但是需要输入 8 个(其实 6 个也可以,gsub 处理到最后落单的斜杠就不转义了)。因为 ruby 读取转义一次,gsub 解析的时候又转义一次。

结论是: 确实很坑

\& 是代入 $&, 相当于 \0 ...

除了 \1, \2, \k<group> , 会代入的就下面这几个了:

$` -- \` 匹配前
$& -- \0, \& 匹配中
$' -- \' 匹配后
7 楼 已删除
匿名 #8 2013年07月19日

#5 楼 @doitian 我写的'&'不是为了转义&,我就是要表达\和&这两个字符。。。啊啊啊~~你们都没看明白我要表达的什么啊

匿名 #9 2013年07月19日

乃们好多人都不仔细看,回复的文不对题

#8 楼 @jamchange 我的意思是你需要替换成 \& 的话,应该去 escape \

'site=|http://jex.im/|.gsub' '|','\\"'

楼主是想吐槽 replacement 里面有 special match chars 是吗 不过 API 都已经明确说明了这些情况了 http://ruby-doc.org/core-1.9.3/String.html#method-i-gsub

匿名 #12 2013年07月22日

#11 楼 @cantin 开始以为 back reference 应该在使用 Regexp 时才会出现,gsub 这样的接口设计显然不合理,几乎所有第一个参数为 string 时,调用方式都应该用content.gsub(str1) {replacement} 才安全。因为参数 str1 是一个已知值,再使用\&这样的 reference 就显多余。 String 应该用一个安全的 replaceAll 方法,一个方法有太多的参数形式,用起来就得格外小心,太不快乐了

@jamchange 你为啥不这么写呢?

    str = 'site=|http://jex.im/|'.gsub(/\|/, "\"")   
=> "site=\"http://jex.im/\""

gsub 方法本来就是把第一个参数当匹配模式来用的。

匿名 #14 2013年08月05日

#13 楼 @blackanger 又是一个不认真看题的。你们需要一双像编译器一样犀利的眼睛

#14 楼 @jamchange 好吧。可能是我没看懂题。

需要 登录 后方可回复, 如果你还没有账号请 注册新账号