21

我的 csv 文件内容在引用的文本中有双引号

test,first,line,"you are a "kind" man",thanks
again,second,li,"my "boss" is you",good

我需要用“”替换每个没有逗号之前或之后的双引号

test,first,line,"you are a ""kind"" man",thanks
again,second,li,"my ""boss"" is you",good

所以 " 被 "" 代替

我试过

x.gsub(/([^,])"([^,])/, "#{$1}\"\"#{$2}")

但没有用

4

2 回答 2

47

您的正则表达式需要更粗一些,以防引号出现在第一个值的开头或最后一个值的结尾:

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')
于 2012-02-01T16:46:20.883 回答
9

假设s是一个字符串,这将起作用:

puts s.gsub(/([^,])"([^,])/, "\\1\"\"\\2")
于 2012-02-01T16:02:22.723 回答