2012-02-01 21 views
16

que tienen contenido del archivo csv con dobles comillas dentro del texto citadoCómo usar ruby ​​gsub Regexp con muchas coincidencias?

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

tengo que sustituir cada comillas dobles no precedida o sucedida por una coma por ""

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

por lo que "se sustituye por" "

me trataron

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

b ut no funcionó

Respuesta

39

Su expresión regular tiene que ser un poco más audaz, en caso de que las cotizaciones se producen al comienzo del primer valor, o al final del último valor:

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" 

la expresión regular anterior se utiliza de búsqueda hacia atrás negativo y afirmaciones de búsqueda hacia delante negativos (anclajes) disponibles en Ruby 1.9.

  • (?<!^|,) - inmediatamente anterior este lugar no debe ser un comienzo de la línea (^) o una coma
  • " - encontrar una cotización doble
  • (?!,|$) - inmediatamente después de este punto no debe ser una coma o al final de la línea ($)

Como beneficio adicional, ya que en realidad no capturar los caracteres de cualquier lado, no es necesario que preocuparse de una Comience usando \1 correctamente en su cadena de reemplazo.

Para obtener más información, consulte la sección "Anclajes" en el official Ruby regex documentation.


Sin embargo, para el caso en que lo hace necesidad de reemplazar partidos en su salida, puede utilizar cualquiera de los siguientes:

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

no puede utilizar cadena de interpolación en el cadena de reemplazo, como lo hizo:

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

... porque eso interpolación de cadenas ocurre una vez, antes de se ejecutó gsub. Utilizando la forma de bloque de gsub vuelve a invocar el bloque para cada coincidencia, momento en el que el $1 global se ha rellenado de forma adecuada y está disponible para su uso.


Editar: para Ruby 1.8 (¿por qué en la tierra que está usando?) Se puede utilizar:

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

Genial, traté de averiguar cómo hacer aseveraciones negativas en Ruby y no pude entenderlo. –

+1

Gracias Phrogz, funciona muy bien con ruby ​​1.9 solamente, ¿puedes aconsejar una respuesta para ruby ​​1.8? –

+0

@MahmoudKhaled Actualizado para trabajar con Ruby 1.8. (En el futuro, si necesita una versión tan antigua de Ruby, incluya esto en su pregunta. Ruby 1.9.1, la primera versión estable de la serie 1.9, se lanzó hace más de tres ** años **.) – Phrogz

8

Suponiendo s es una cadena, esto va a funcionar:

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

Al utilizar comillas dobles en el contenido, es probable que sea mejor utilizar comillas simples para enquote ellos como ' '\ 1 '' \ 2'' o utilizar la tercera forma'% q [\ 1 "" \ 2] ' – tadman

+1

Estoy un poco preocupado de que mi respuesta no sea adecuada para su situación porque no maneja muchas cosas, como si realmente hubiera una coma al lado de una cita en sus datos. Es posible que deba hacer algo más complicado que no esté basado en expresiones regulares. –

Cuestiones relacionadas