Как использовать Ruby Gsub Regexp со многими матчами?

у меня есть содержимое файла 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(/([^,])"([^,])/, "#{}""#{}")

но не работает

2 ответов


ваше регулярное выражение должно быть немного более жирным, если кавычки происходят в начале первого значения или в конце последнего значения:

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.

  • (?<!^|,) - непосредственно перед этим местом не должно быть ни начала строки (^) или запятая
  • " - найти двойника цитата
  • (?!,|$) - сразу после этого места не должно быть ни запятой, ни конца строки ($)

в качестве бонуса, так как вы на самом деле не захватили персонажей с обеих сторон, вам не нужно беспокоиться об использовании правильно в строке замены.

для получения дополнительной информации см. раздел "якоря" в официальная документация Ruby regex.


однако, для случая, когда вы do нужно заменить спички в вашем выходе, вы можете использовать любое из следующего:

"hello".gsub /([aeiou])/, '<>'            #=> "h<e>ll<o>"
"hello".gsub /([aeiou])/, "<\1>"           #=> "h<e>ll<o>"
"hello".gsub(/([aeiou])/){ |m| "<#{}>" }  #=> "h<e>ll<o>"

вы не можете использовать интерполяцию строк в строке замены, как вы сделали:

"hello".gsub /([aeiou])/, "<#{}>"
 #=> "h<previousmatch>ll<previousmatch>"

...потому что эта интерполяция строк происходит один раз,до the gsub была запущена. Используя блочную форму gsub повторно вызывает блок для каждого матча, в этот момент глобальный был надлежащим образом заполнен и доступный для использования.


редактировать: для Ruby 1.8 (почему вы это используете?) вы можете использовать:

puts csv.gsub(/([^,\n\r])"([^,\n\r])/,'""')

предполагая, что s является строкой, это будет работать:

puts s.gsub(/([^,])"([^,])/, "\1\"\"\2")