您的正则表达式需要更粗一些,以防引号出现在第一个值的开头或最后一个值的结尾:
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"
上面的正则表达式使用了 Ruby 1.9 中可用的否定后向和否定前瞻断言(锚)。
(?<!^|,)
— 紧接在此位置之前不得有行首 ( ^
) 或逗号
"
— 找到一个双引号
(?!,|$)
— 紧跟在此位置之后,不得有逗号或行尾 ( $
)
作为奖励,由于您实际上并未捕获任一侧的字符,因此您无需担心\1
在替换字符串中正确使用。
有关更多信息,请参阅官方 Ruby 正则表达式文档中的“锚点”部分。
但是,对于您确实需要替换输出中的匹配项的情况,您可以使用以下任何一种:
"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')