Как использовать ruby ​​gsub Regexp со многими совпадениями? - PullRequest
19 голосов
/ 01 февраля 2012

У меня есть содержимое файла 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 ]

44 голосов
/ 01 февраля 2012

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

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 .


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

"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')
9 голосов
/ 01 февраля 2012

Если s строка, это будет работать:

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