Pregunta

Estoy usando awk para llevar a cabo contando 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

Yo estaba usando este script awk para contar la suma:

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

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

Gracias.

¿Fue útil?

Solución

que escribir una función en awk, como a continuación:

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

se puede incorporar en el script esta función y comprobar si el tercer campo es numérico o not.If no es numérico y luego ir por el cuarto campo y si el campo inturn cuarto no es ir Numberic para el 5 de ... hasta llegar a una numérica value.probably un bucle ayudará aquí, y añadirlo a la suma.

Otros consejos

Una forma usando GNU awk y FPAT

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

Resultados:

192

Usted es probablemente mejor de hacerlo en Perl con texto :: CSV, ya que es una solución rápida y robusta.

Puede ayudar a awk trabajo con campos de datos que contienen comas (o saltos de línea) mediante el uso de un pequeño guión que escribí llamado csvquote. Sustituye a los infractores comas dentro de los campos citados con caracteres no imprimibles. Si es necesario, se puede restaurar posteriormente esas comas -. Pero en este caso, no es necesario

Este es el comando:

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

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

Estoy utilizando

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

para definir los campos con gawk. He descubierto que cuando el campo es nulo este no reconoce el número correcto de campos. Debido a que "+" requiere al menos 1 carácter en el campo. He cambiado a:

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

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

También encontrará 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

Para tan simple un archivo de entrada como de que sólo se puede escribir una pequeña función para convertir todos los bienes fuera del SFS de las cotizaciones a algún otro valor (I eligió RS desde el separador de registro no puede ser parte del registro) y luego uso que 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>

Sólo se complica cuando hay que hacer frente a las nuevas líneas incrustadas y citas escapado incrustados dentro de las comillas e incluso entonces no es demasiado duro y todo se ha hecho antes ...

¿Cuál es la forma más robusta para CSV eficiente de análisis usando awk? para más información.

Siempre se puede abordar el problema de la fuente. Poner comillas alrededor del campo de nombre, al igual que el campo de "yo soy el, pregunta". Esto es mucho más fácil que pasar sus soluciones de codificación de tiempo para eso.

Actualizar (como se solicitó Dennis). Un ejemplo sencillo

$ 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 se puede ver, al establecer el delimitador de comillas dobles, los campos que pertenecen a las "comillas" están siempre en número par. Desde OP no tiene el lujo de la modificación de los datos de origen, este método no será apropiado para él.

Si usted sabe con certeza 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

Este artículo no ayuda a resolver este mismo problema campo de datos. La mayoría CSV pondrá una cita alrededor de los campos con espacios o comas dentro de ellos. Esta meta la pata del campo cuentan para awk menos que filtre hacia fuera.

Si necesita los datos dentro de los campos que contienen la basura, esto no es para ti. ghostdog74 dio la respuesta, que desemboca ese campo, pero mantiene el recuento de campo total, al final, lo que 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 he usado. El puño tres campos nunca han tenido este problema en los datos. El campo de cuarto que contiene el nombre del cliente a menudo, pero necesitaba que los datos. El resto de campos que presentan el problema de que pudiera tirar a la basura sin ningún problema, ya que no se necesitaba en mi salida del informe. Así que primero sed hacia fuera la basura del cuarto campo muy específica y retire los dos primeros casos de cotizaciones. Luego aplico lo ghostdog74gave para vaciar los campos restantes que tienen comas dentro de ellos - esto también elimina las comillas, pero yo uso printfto mantener los datos en un solo registro. Empiezo con 85 campos y termino con 85 campos en todos los casos de mis 8000+ líneas de datos desordenado. 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 sino que también mantiene el registro, por supuesto, es:

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

megas de agradecimiento a ghostdog74 para la gran solución!

NetsGuy256 /

FPAT es la solución elegante, ya que puede manejar las comas temidos dentro problema cotizaciones, sino para sumar una columna de números en la última columna, independientemente del número de separadores anterior, $ NF funciona bien:

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

Para acceder a la segunda a la última columna, se utilizaría la siguiente:

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

CSV analizadores de pleno derecho como Text::CSV_XS de Perl son especialmente diseñada para manejar ese tipo de rareza.

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

se necesita

allow_whitespace ya que los datos de entrada tiene un espacio en blanco que rodea a los separadores de coma. Muy versiones antiguas de Text::CSV_XS no admiten esta opción.

he proporcionado una explicación más detallada de Text::CSV_XS dentro de mi respuesta aquí: analizar el archivo CSV usando gawk

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top