Elimine solo las comas presentes entre números entre comillas dobles en un archivo csv

Elimine solo las comas presentes entre números entre comillas dobles en un archivo csv

En un archivo de texto, quiero eliminar ,(comas) y también "(comillas) (solo si las comillas dobles contienen números separados por comas).

56,72,"12,34,54",x,y,"foo,a,b,bar"

Salida esperada

56,72,123454,x,y,"foo,a,b,bar"

Nota:Muestro la línea anterior solo como un ejemplo. Mi archivo de texto contiene muchas líneas como las anteriores y los números separados por comas presentes entre comillas dobles deben variar. Eso es,

56,72,"12,34,54",x,y,"foo,a,b,bar"
56,92,"12,34",x,y,"foo,a,b,bar"
56,72,"12,34,54,78,76,54,67",x,y,"foo,a,b,bar"
56,72,x,y,"foo,a,b,bar","12,34,54"
56,72,x,y,"foo,a,b,bar","12,34,54","45,57,84,92","bar,foo"

Rendimiento esperado:

56,72,123454,x,y,"foo,a,b,bar"
56,92,1234,x,y,"foo,a,b,bar"
56,72,12345478765467,x,y,"foo,a,b,bar"
56,72,x,y,"foo,a,b,bar",123454
56,72,x,y,"foo,a,b,bar",123454,45578492,"bar,foo"

Hay nvarios números presentes entre comillas dobles separados por comas. Y también deje las comillas dobles que contienen caracteres tal como están.

Me encanta sedla herramienta de procesamiento de texto. Me alegraría que publicaras alguna sedsolución para esto.

Respuesta1

Si Perl está bien, aquí hay una forma breve (y probablemente rápida, aunque no necesariamente simple :)) de hacerlo:

perl -pe 's:"(\d[\d,]+)":$1=~y/,//dr:eg' file

El eindicador del s:::operador (que es simplemente otra forma de escribir s///) hace que el reemplazo se trate como una expresión que se evalúa cada vez. Esa expresión toma la $1captura de la expresión regular (a la que ya le faltan las comillas) y la traduce ( y///, que también se puede escribir como tr///) eliminando ( /d) todas las comas. La rbandera to yes necesaria para que el valor sea la cadena traducida, en lugar del recuento de traducciones.

Para aquellos que de alguna manera se sienten mancillados por Perl, aquí está el equivalente en Python. En realidad, Python no es una herramienta de shell de una sola línea, pero a veces se le puede engatusar para que coopere. Lo siguiente se puede escribir como una línea (a diferencia de forlos bucles, que no se pueden escribir), pero el desplazamiento horizontal lo hace (aún más) ilegible:

python -c '
import re;
import sys;
r=re.compile("\"(\d+(,\d+)*)\"");
all(not sys.stdout.write(r.sub(lambda m:m.group(1).replace(",",""),l))
    for l in sys.stdin)
' < file

Respuesta2

Esto (adaptado deaquí) debería hacer lo que necesitas, aunque el Perl de @rici es mucho más simple:

$ sed -r ':a;s/(("[0-9,]*",?)*"[0-9,]*),/\1/;ta; s/""/","/g; 
          s/"([0-9]*)",?/\1,/g ' file
56,72,123454,x,y,"foo,a,b,bar"
56,92,1234,x,y,"foo,a,b,bar"
56,72,12345478765467,x,y,"foo,a,b,bar"
56,72,x,y,"foo,a,b,bar",123454,
56,72,x,y,"foo,a,b,bar",123454,45578492,"bar,foo"

Explicación

  • :a: define una etiqueta llamada a.
  • s/(("[0-9,]*",?)*"[0-9,]*),/\1/: Este necesita ser descompuesto
    • En primer lugar, utilizando esta construcción (foo(bar)): \1será foobary \2será bar.
    • "[0-9,]*",?: coincide con 0 o más de 0-9o ,, seguido de 0 o 1 ,.
    • ("[0-9,]*",?)*: coincide con 0 o más de los anteriores.
    • "[0-9,]*: coincide con 0 o más de 0-9o ,que vienen justo después de un"
  • ta;: vuelve a la etiqueta ay ejecuta de nuevosila sustitución fue exitosa.
  • s/""/","/g;: Postprocesamiento. Reemplazar ""con ",".
  • s/"([0-9]*)",?/\1,/g: elimina todas las comillas alrededor de los números.

Esto podría ser más fácil de entender con otro ejemplo:

$ echo '"1,2,3,4"' | sed -nr ':a;s/(("[0-9,]*",?)*"[0-9,]*),/\1/;p;ta;'
"1,2,34"
"1,234"
"1234"
"1234"

Entonces, si bien puedes encontrar un número que está justo después de una comilla y seguido de una coma y otro número, une los dos números y repite el proceso hasta que ya no sea posible.

En este punto creo que es útil mencionar una cita que info sedaparece en la sección que describe funciones avanzadas, como la etiqueta utilizada anteriormente (gracias por encontrar a @Braiam):

En la mayoría de los casos, el uso de estos comandos indica que probablemente sea mejor programar en algo como `awk' o Perl.

Respuesta3

Para datos CSV, usaría un lenguaje con un analizador CSV real. Por ejemplo con Rubí:

ruby -rcsv -pe '
  row = CSV::parse_line($_).map {|e| e.delete!(",") if e =~ /^[\d,]+$/; e} 
  $_  = CSV::generate_line(row)
' <<END
56,72,"12,34,54",x,y,"foo,a,b,bar"
56,92,"12,34",x,y,"foo,a,b,bar"
56,72,"12,34,54,78,76,54,67",x,y,"foo,a,b,bar"
56,72,x,y,"foo,a,b,bar","12,34,54"
56,72,x,y,"foo,a,b,bar","12,34,54","45,57,84,92","bar,foo"
END
56,72,123454,x,y,"foo,a,b,bar"
56,92,1234,x,y,"foo,a,b,bar"
56,72,12345478765467,x,y,"foo,a,b,bar"
56,72,x,y,"foo,a,b,bar",123454
56,72,x,y,"foo,a,b,bar",123454,45578492,"bar,foo"

Respuesta4

Usandorakú(anteriormente conocido como Perl_6)

~$ raku -pe 's:g/ \" ~ \" (\d+) ** 2..* % "," /{$0.join}/;'  file

Entrada de muestra:

56,72,"12,34,54",x,y,"foo,a,b,bar"
56,92,"12,34",x,y,"foo,a,b,bar"
56,72,"12,34,54,78,76,54,67",x,y,"foo,a,b,bar"
56,72,x,y,"foo,a,b,bar","12,34,54"
56,72,x,y,"foo,a,b,bar","12,34,54","45,57,84,92","bar,foo"

Salida de muestra:

56,72,123454,x,y,"foo,a,b,bar"
56,92,1234,x,y,"foo,a,b,bar"
56,72,12345478765467,x,y,"foo,a,b,bar"
56,72,x,y,"foo,a,b,bar",123454
56,72,x,y,"foo,a,b,bar",123454,45578492,"bar,foo"

Raku es un lenguaje de programación de la familia Perl con una serie de potentes funciones de expresiones regulares. Consulte la URL a continuación para obtener una descripción general de esta respuesta:

https://unix.stackexchange.com/a/722570/227738

En el código anterior, se reconocen los dígitos y se eliminan las comas incrustadas. La expresión regular aprovecha el hecho de queestructuras anidadasse puede denotar con la nueva notación ~ tilde (anidada) de Raku, de modo que \" ~ \" [\d+]significa "uno o más dígitos entre "comillas dobles".

Además,estructuras repetidasse puede denotar con el nuevo %cuantificador modificado de Raku para estructuras repetidas. La notación [\d+] ** 2..* % "," significa "uno o más dígitos separados por ,comas con este patrón repetido ** 2..*dos o más veces. [Si hay un separador final (p. ej. coma), use a %%en lugar de %en la sintaxis].

Esto es sólo un comienzo. Los archivos CSV con separadores alternativos, nuevas líneas incrustadas, comas incrustadas, campos potencialmente en blanco, etc., realmente necesitan ser manejados por un verdadero analizador CSV como Text::CSVel módulo de Raku. Consulte los enlaces a continuación para obtener más detalles.

https://docs.raku.org/language/regexes
https://raku.land/github:Tux/Text::CSV
https://raku.org

información relacionada