2012-02-01 8 views
16

devo contenuto del file CSV con le virgolette doppie all'interno testo citatoCome usare ruby ​​gsub Regexp con molte partite?

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

ho bisogno di sostituire ogni doppio apice non preceduti o seguiti da una virgola da ""

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

così "è sostituito da" "

ho cercato

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

b ut non ha funzionato

risposta

39

tuo regex ha bisogno di essere un po 'più audace, nel caso in cui le quotazioni si verificano all'inizio del primo valore, o alla fine dell'ultimo valore:

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" 

Quanto sopra regex utilizza lookbehind negativo e asserzioni lookahead negativo (ancore) disponibili in Ruby 1.9.

  • (?<!^|,) - immediatamente precedente questo posto non ci deve essere un inizio di riga (^) o una virgola
  • " - trovare una doppia citazione
  • (?!,|$) - subito dopo questo posto non ci deve essere o una virgola o alla fine della linea ($)

Come bonus, dal momento che non hai realmente catturare i caratteri su entrambi i lati, non c'è bisogno di preoccuparsi di un utilizzare \1 correttamente nella stringa di sostituzione.

Per ulteriori informazioni, consultare la sezione "Ancoraggi" nello official Ruby regex documentation.


Tuttavia, per il caso in cui si fai necessità di sostituire le partite in uscita, è possibile utilizzare uno dei seguenti:

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

Non è possibile utilizzare String interpolazione nel stringa di sostituzione, come avete fatto:

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

... perché l'interpolazione stringa accade una volta, prima di è stato eseguito il gsub. L'utilizzo del modulo di blocco di gsub richiama nuovamente il blocco per ogni corrispondenza, a quel punto il numero globale $1 è stato popolato correttamente ed è disponibile per l'uso.


Edit: per Ruby 1.8 (perché sulla terra stai usando questo?) È possibile utilizzare:

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

Fresco, ho cercato di capire come fare le affermazioni negative in Ruby e non riuscivo a capirlo. –

+1

Grazie a Phrogz, funziona perfettamente solo con Ruby 1.9, puoi consigliare una risposta per Ruby 1.8? –

+0

@MahmoudKhaled Aggiornato per funzionare con Ruby 1.8. (In futuro, se hai bisogno di una versione così antica di Ruby, includi questo nella tua domanda: Ruby 1.9.1, la prima versione stabile della serie 1.9, è stata rilasciata in tre ** ** anni fa.) – Phrogz

8

Supponendo s è una stringa, questo funzionerà:

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

Quando si utilizza le virgolette doppie nel contenuto, è probabilmente meglio utilizzare le virgolette singole per enquote loro come ' '\ 1 '' \ 2'' o utilizzare la terza forma'% q [\ 1 "" \ 2] ' – tadman

+1

Sono un po 'preoccupato che la mia risposta non sia adeguata alla situazione perché non gestisce molte cose, come se ci fosse una virgola accanto ad una cita nei tuoi dati. Potrebbe essere necessario fare qualcosa di più complicato che non sia basato sulla regex. –