Domanda

Sto usando awk per eseguire contare la somma di una colonna nel file CSV. Il formato dei dati è qualcosa di simile:

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

Io sto usando questo script awk per contare la somma:

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

Alcuni del valore nel campo Nome contiene virgola e questa pausa il mio script awk. La mia domanda è: può awk risolvere questo problema? Se sì, e come posso farlo?

Grazie.

È stato utile?

Soluzione

si scrive una funzione in awk come di seguito:

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

è possibile incorporare nello script di questa funzione e controllare se il terzo campo è numerico o NOT.IF non numerico poi andare per il 4 ° campo e se il campo inturn 4 ° non è andare numberic per il 5 ° ... fino a raggiungere un numerica value.probably un ciclo aiuterà qui, e aggiungerlo alla somma.

Altri suggerimenti

Un modo utilizzando GNU awk e FPAT

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

Risultato:

192

Sei probabilmente meglio farlo in Perl con il testo :: CSV, dato che è una soluzione veloce e robusto.

Si può aiutare awk i campi di dati che contengono le virgole (o ritorni a capo) utilizzando un piccolo script che ho scritto chiamato csvquote. Esso sostituisce le virgole offendere all'interno campi citati con caratteri non stampabili. Se è necessario, in seguito sarà possibile ripristinare quelle virgole -. Ma in questo caso, non è necessario

Ecco il comando:

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

https://github.com/dbro/csvquote per il codice

sto usando

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

per definire i campi con gawk. Ho trovato che quando il campo è nullo questo non riconosce corretto numero di campi. Perché "+" richiede almeno 1 carattere nel campo. Ho cambiato in:

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

e sostituirlo con "+" "*". Esso funziona correttamente.

Trovo anche che Guida GNU Awk l'utente ha anche questo problema. https://www.gnu.org/software/gawk /manual/html_node/Splitting-By-Content.html

Per come semplice file di input, come che si può solo scrivere una piccola funzione per convertire tutto il reale al di fuori FS delle citazioni per qualche altro valore (ho scelto RS poiché il separatore di record non può far parte del record) e poi l'uso che come FS, ad esempio:

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

E 'solo si complica quando si ha a che fare con a capo incorporati e citazioni sfuggiti incorporati all'interno delle citazioni e anche allora non è troppo duro e tutto è stato fatto prima ...

Qual è il modo più robusto per efficiente CSV parse utilizzando awk? per ulteriori informazioni.

È sempre possibile affrontare il problema alla fonte. Mettere le virgolette intorno al campo del nome, proprio come il campo di "Io sono il, domanda". Questo è molto più facile che spendere il vostro tempo di codifica soluzioni alternative per questo.

Aggiorna (come Dennis richiesto). Un semplice esempio

$ 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

Come si può vedere, impostando il delimitatore di virgolette, i campi che appartengono ai "virgolette" sono sempre in numero pari. Poiché OP non ha il lusso di modificare i dati di origine, questo metodo non sarà il caso.

Se si sa per certo che la colonna 'valore' è sempre l'ultima colonna:

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

NF rappresenta il numero di campi, in modo da $ NF è l'ultima colonna

Questo articolo ha aiutato a risolvere questo stesso problema campo di dati. La maggior parte CSV metterà un preventivo intorno campi con spazi o virgole al loro interno. Questo scombina il campo contano per awk a meno che non filtrare fuori.

Se avete bisogno dei dati all'interno di quei campi che contengono la spazzatura, questo non è per voi. ghostdog74 fornito la risposta, che svuota quel campo ma mantiene il conteggio campo totale, alla fine, che è la chiave per mantenere l'uscita dati coerenti. Non mi piaceva come questa soluzione ha introdotto nuove linee. Questa è la versione di questa soluzione che ho usato. Il pugno tre campi mai avuto questo problema nei dati. Il campo di quarta che contiene il nome del cliente spesso ha fatto, ma avevo bisogno di quei dati. I restanti campi che presentano il problema potevo buttare via senza problema perché non era necessario nel mio emissione del report. Così ho sed fuori spazzatura il 4 ° del campo in modo molto specifico e rimuovere le prime due istanze di citazioni. Poi applico quello che ghostdog74gave per svuotare i campi rimanenti che hanno le virgole al loro interno - questo rimuove anche le virgolette, ma io uso printfto mantenere i dati in un singolo record. Comincio con 85 campi e finire con 85 campi in tutti i casi da parte dei miei 8000+ righe di dati disordinato. Un punteggio perfetto!

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 soluzione che svuota i campi con le virgole al loro interno, ma mantiene anche il record, ovviamente è:

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

mega di grazie al ghostdog74 per la grande soluzione!

NetsGuy256 /

FPAT è la soluzione elegante perché è in grado di gestire le virgole temute all'interno problema citazioni, ma per riassumere una colonna di numeri nell'ultima colonna a prescindere dal numero di precedenti separatori, $ NF funziona bene:

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

Per accedere alla penultima colonna, si può usare questa:

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

parser CSV a tutti gli effetti, come Text::CSV_XS di Perl sono costruiti appositamente per gestire questo tipo di stranezza.

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

è necessario

allow_whitespace poiché i dati di ingresso ha spazi che lo circondano i separatori virgola. Molto vecchie versioni di Text::CSV_XS potrebbero non supportare questa opzione.

ho fornito una spiegazione più approfondita di Text::CSV_XS nel mio risposta qui: parse csv file utilizzando gawk

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top