2012-02-01 13 views
16

mam csv zawartość pliku o cudzysłów wewnątrz cytowanego tekstuJak korzystać z ruby ​​gsub Regexp z wieloma meczami?

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

muszę wymienić każdy podwójny cudzysłów nie poprzedzone lub udało się przecinkiem przez „”

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

tak „zastępuje się wyrazami” "

próbowałem

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

b ut nie działa

Odpowiedz

39

Twój regex musi być nieco bardziej śmiały, w przypadku cytaty nastąpić na początku pierwszej wartości, lub na końcu ostatniej wartości:

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" 

powyższy regex wykluczających lookbehind i negatywnych potwierdzeń wyprzedzającej (jarzma), są dostępne w Ruby 1.9.

  • (?<!^|,) - bezpośrednio poprzedzającym to miejsce nie musi być początek linii (^) lub przecinek
  • " - znaleźć podwójny cudzysłów
  • (?!,|$) - bezpośrednio po tym miejscu nie musi być przecinek lub koniec linii ($)

Jako bonus, ponieważ w rzeczywistości nie uchwyciłeś postaci po obu stronach, nie musisz się martwić podążając poprawnie za pomocą \1 w ciągu zastępującym.

Aby uzyskać więcej informacji, zobacz sekcję "Kotwy" w official Ruby regex documentation.


Jednakże w przypadku, gdy zrobić konieczność wymiany mecze w swojej mocy, można użyć dowolnej z następujących czynności:

"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>" 

Nie można użyć interpolacji String w wymiana łańcuch, jak to było:

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

... dlatego, że interpolacja ciąg zdarza się raz, przed został uruchomiony gsub. Użycie formularza blokowego gsub ponownie wywołuje blok dla każdego dopasowania, w którym to momencie globalna $1 została poprawnie wypełniona i jest dostępna do użytku.


Edit: Ruby 1.8 (dlaczego na ziemi, które używasz?) możesz użyć:

+0

Fajnie, próbowałem wymyślić, jak wykonać negatywne twierdzenia lookbehind w Ruby i nie mogłem tego rozgryźć. –

+1

Dzięki Phrogz, działa świetnie tylko z rubinowym 1.9, czy możesz udzielić odpowiedzi na ruby ​​1.8? –

+0

@MahmoudKhaled Zaktualizowano do pracy z Ruby 1.8. (W przyszłości, jeśli potrzebujesz tak starej wersji Rubiego, proszę dołącz to do swojego pytania Ruby 1.9.1 - pierwsza stabilna wersja serii 1.9 została wydana w ciągu trzech ** lat ** temu.) – Phrogz

8

Zakładając s jest ciągiem znaków, to będzie działać:

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

Kiedy używasz podwójnych cudzysłowów w treści, prawdopodobnie lepiej jest użyć pojedynczych cudzysłowów, aby zakodować je jako ''\ 1" "\ 2'' lub użyj trzeciego formularza'% q [\ 1 "" \ 2] ' – tadman

+1

Obawiam się, że moja odpowiedź nie będzie odpowiednia dla twojej sytuacji, ponieważ nie obsługuje wielu rzeczy, na przykład, jeśli w rzeczywistości jest przecinek obok zacytuj w swoich danych. Może być konieczne zrobienie czegoś bardziej skomplikowanego, które nie jest oparte na regex. –