Как использовать 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(/([^,])"([^,])/, "#{$1}\"\"#{$2}")
но не сработало
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.
(?<!^|,)
- непосредственно перед этим местом не должно быть ни начала строки (^
) или запятая"
- найти двойную цитату(?!,|$)
- сразу после этого места не должно быть ни запятой, ни конца строки ($
)
В качестве бонуса, поскольку вы на самом деле не захватывали персонажей с обеих сторон, вам не нужно беспокоиться об использовании \1
правильно в строке замены.
Для получения дополнительной информации см. Раздел "Якоря" в официальной документации Ruby regex.
Однако для случая, когда вам нужно заменить совпадения в выходных данных, вы можете использовать любое из следующего:
"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')
Если предположить, s
это строка, это будет работать:
puts s.gsub(/([^,])"([^,])/, "\\1\"\"\\2")