csv awk field text-parsing quoting

¿Puede awk tratar con un archivo CSV que contiene una coma dentro de un campo entre comillas?



field text-parsing (11)

Este artículo me ayudó a resolver este mismo problema de campo de datos. La mayoría de CSV colocará una cita alrededor de los campos con espacios o comas dentro de ellos. Esto desordena el recuento de campos para awk a menos que los filtre.

Si necesita los datos dentro de esos campos que contienen la basura, esto no es para usted. ghostdog74 proporcionó la respuesta, que vacía ese campo pero mantiene el recuento total de campos al final, lo cual es clave para mantener la salida de datos consistente. No me gustó cómo esta solución introdujo nuevas líneas. Esta es la versión de esta solución que utilicé. Los tres primeros campos nunca tuvieron este problema en los datos. El cuarto campo que contenía el nombre del cliente a menudo lo hacía, pero necesitaba esos datos. Los campos restantes que exhiben el problema se podrían tirar sin problema porque no fue necesario en la salida de mi informe. Así que primero eliminé la basura del 4to campo muy específicamente y elimino las dos primeras instancias de citas. Luego aplico lo que ghostdog74 proporcionó para vaciar los campos restantes que tienen comas dentro de ellos; esto también elimina las comillas, pero uso printf para mantener los datos en un solo registro. Empiezo con 85 campos y termino con 85 campos en todos los casos de mis más de 8000 líneas de datos desordenados. Una puntuación perfecta!

grep -i $1 $dbfile | sed ''s//, Inc.//;s/, LLC.//;s/, LLC//;s/, Ltd.//;s//"//;s//"//'' | awk -F''"'' ''{ for(i=1;i<=NF;i+=2) printf ($i);printf ("/n")}'' > $tmpfile

La solución que vacía los campos con comas dentro de ellos, pero también mantiene el registro, por supuesto, es:

awk -F''"'' ''{ for(i=1;i<=NF;i+=2) printf ($i);printf ("/n")}

¡Muchas gracias a ghostdog74 por la gran solución!

NetsGuy256 /

Estoy usando awk para realizar el conteo de la suma de una columna en el archivo csv. El formato de datos es algo como:

id, name, value 1, foo, 17 2, bar, 76 3, "I am the, question", 99

Estaba usando este script awk para contar la suma:

awk -F, ''{sum+=$3} END {print sum}''

Parte del valor en el campo de nombre contiene una coma y esto rompe mi script awk. Mi pregunta es: ¿puede awk resolver este problema? Si es así, ¿y cómo puedo hacer eso?

Gracias.


FPAT es la solución elegante porque puede manejar las comas temidas dentro del problema de las comillas, pero para sumar una columna de números en la última columna, independientemente del número de separadores anteriores, $ NF funciona bien:

awk -F"," ''{sum+=$NF} END {print sum}''

Para acceder a la segunda a la última columna, usaría esto:

awk -F"," ''{sum+=$(NF-1)} END {print sum}''


Los analizadores CSV completos, como Text::CSV_XS Perl, están diseñados para manejar ese tipo de rarezas.

perl -MText::CSV_XS -lne ''BEGIN{$csv=Text::CSV_XS->new({allow_whitespace => 1})} if($csv->parse($_)){@f=$csv->fields();$sum+=$f[2]} END{print $sum}'' file

allow_whitespace es necesario ya que los datos de entrada tienen espacios en blanco alrededor de los separadores de coma. Las versiones muy antiguas de Text::CSV_XS pueden no admitir esta opción.

Proporcioné más explicaciones de Text::CSV_XS en mi respuesta aquí: analizar archivo csv usando gawk


Para un archivo de entrada tan simple como usted solo puede escribir una pequeña función para convertir todos los FS reales fuera de las comillas a algún otro valor (elegí RS porque el separador de registros no puede ser parte del registro) y luego usarlo como el FS, por ejemplo:

$ cat decsv.awk BEGIN{ fs=FS; FS=RS } { decsv() for (i=1;i<=NF;i++) { printf "Record %d, Field %d is <%s>/n" ,NR,i,$i } print "" } function decsv( curr,head,tail) { tail = $0 while ( match(tail,/"[^"]+"/) ) { head = substr(tail, 1, RSTART-1); gsub(fs,RS,head) curr = curr head substr(tail, RSTART, RLENGTH) tail = substr(tail, RSTART + RLENGTH) } gsub(fs,RS,tail) $0 = curr tail } $ cat file id, name, value 1, foo, 17 2, bar, 76 3, "I am the, question", 99 $ awk -F", " -f decsv.awk file Record 1, Field 1 is <id> Record 1, Field 2 is <name> Record 1, Field 3 is <value> Record 2, Field 1 is <1> Record 2, Field 2 is <foo> Record 2, Field 3 is <17> Record 3, Field 1 is <2> Record 3, Field 2 is <bar> Record 3, Field 3 is <76> Record 4, Field 1 is <3> Record 4, Field 2 is <"I am the, question"> Record 4, Field 3 is <99>

Solo se complica cuando tiene que lidiar con nuevas líneas incrustadas y citas escapadas incrustadas dentro de las comillas, e incluso entonces no es demasiado difícil y todo se ha hecho antes ...

Consulte ¿Cuál es la forma más robusta de analizar CSV de manera eficiente utilizando awk? para más información.


Probablemente sea mejor hacerlo en Perl con Text :: CSV, ya que es una solución rápida y robusta.


Puede ayudar a awk a trabajar con campos de datos que contengan comas (o nuevas líneas) usando un pequeño script que escribí llamado csvquote. Reemplaza las comas ofensivas dentro de los campos entre comillas con caracteres no imprimibles. Si lo necesita, puede restaurar esas comas más adelante, pero en este caso, no necesita hacerlo.

Aquí está el comando:

csvquote inputfile.csv | awk -F, ''{sum+=$3} END {print sum}''

vea https://github.com/dbro/csvquote para el código


Si está seguro de que la columna ''valor'' es siempre la última columna:

awk -F, ''{sum+=$NF} END {print sum}''

NF representa el número de campos, por lo que $ NF es la última columna


Siempre se puede abordar el problema desde la fuente. Ponga comillas alrededor del campo del nombre, al igual que el campo "Yo soy la pregunta". Esto es mucho más fácil que gastar su tiempo en la codificación de soluciones para eso.

Actualización (como Dennis pidió). Un ejemplo simple

$ s=''id, "name1,name2", value 1, foo, 17 2, bar, 76 3, "I am the, question", 99'' $ echo $s|awk -F''"'' ''{ for(i=1;i<=NF;i+=2) print $i}'' id, , value 1, foo, 17 2, bar, 76 3, , 99 $ echo $s|awk -F''"'' ''{ for(i=2;i<=NF;i+=2) print $i}'' name1,name2 I am the, question

Como puede ver, al configurar el delimitador para comillas dobles, los campos que pertenecen a las "comillas" están siempre en número par. Como OP no tiene el lujo de modificar los datos de origen, este método no será apropiado para él.


Una forma usando GNU awk y FPAT

awk ''BEGIN { FPAT = "([^, ]+)|(/"[^/"]+/")" } { sum+=$3 } END { print sum }'' file.txt

Resultado:

192


escribes una función en awk como abajo:

$ awk ''func isnum(x){return(x==x+0)}BEGIN{print isnum("hello"),isnum("-42")}'' 0 1

puede incorporar en su script esta función y verificar si el tercer campo es numérico o no. Si no es numérico, vaya al cuarto campo y si la entrada del cuarto campo no es numérica, vaya al quinto ... hasta que alcance un valor numérico. probablemente un bucle ayude aquí, y agregarlo a la suma.


estoy usando

`FPAT="([^,]+)|(/"[^/"]+/")" `

Para definir los campos con gawk. Encontré que cuando el campo es nulo, este no reconoce el número correcto de campos. Porque "+" requiere al menos 1 carácter en el campo. Lo cambié a

`FPAT="([^,]*)|(/"[^/"]*/")"`

y reemplace "+" con "*" . Funciona correctamente

También encuentro que la Guía del usuario de GNU Awk también tiene este problema. https://www.gnu.org/software/gawk/manual/html_node/Splitting-By-Content.html