你的正则表达式需要多一点大胆,万一出现报价在第一值的开始,或在最后值的末尾:
csv = <<ENDCSV
test,first,line,"you are a "kind" man",thanks
again,second,li,"my "boss" is you",good
more,""Someone" said that you're "cute"",yay
"watch out for this",and,also,"this test case"
ENDCSV
puts csv.gsub(/(?<!^|,)"(?!,|$)/,'""')
#=> test,first,line,"you are a ""kind"" man",thanks
#=> again,second,li,"my ""boss"" is you",good
#=> more,"""Someone"" said that you're ""cute""",yay
#=> "watch out for this",and,also,"this test case"
上述正则表达式是使用负回顾后和排除模式断言(锚)在红宝石1.9可用。
(?<!^|,)
- 双引号
(?!,|$)
发现 - - 这点紧前必须不存在或者是线(^
)的开始或逗号
"
立即这个点以下必须不存在任逗号或行尾($
)
作为奖励,因为您实际上并未捕获任何一方的角色,所以您不必担心在替换字符串中正确使用\1
。
欲了解更多信息,请参阅official Ruby regex documentation中的“锚”部分。
但是,对于你做需要更换你的输出匹配的情况下,你可以使用任何的以下内容:
"hello".gsub /([aeiou])/, '<\1>' #=> "h<e>ll<o>"
"hello".gsub /([aeiou])/, "<\\1>" #=> "h<e>ll<o>"
"hello".gsub(/([aeiou])/){ |m| "<#{$1}>" } #=> "h<e>ll<o>"
你不能在使用字符串插值替换字符串,像你一样:
"hello".gsub /([aeiou])/, "<#{$1}>"
#=> "h<previousmatch>ll<previousmatch>"
...因为这串插发生一次,在之前gsub
已经运行。使用的gsub
块形式再调用用于每个匹配,在该点的全球$1
已经适当地填充,并且可使用的块。
编辑:为Ruby 1.8(为什么地球上您使用的是什么?)你可以使用:
puts csv.gsub(/([^,\n\r])"([^,\n\r])/,'\1""\2')
很酷,我试图弄清楚如何在Ruby中做负面的lookbehind断言,并且无法弄清楚。 – 2012-02-01 17:09:30
感谢Phrogz,它仅适用于Ruby 1.9,能否为Ruby 1.8提供建议? – 2012-02-04 16:15:25
@MahmoudKhaled更新后与Ruby 1.8一起工作。 (未来,如果您需要使用如此古老的Ruby版本,请在您的问题中提及这一点。Ruby 1.9.1是1.9系列的第一个稳定版本,已于三年前发布**。 ) – Phrogz 2012-02-04 18:04:46