Question

J'utilise awk pour effectuer le comptage de la somme d'une colonne dans le fichier csv. Le format de données est quelque chose comme:

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

J'utilisais ce script awk compter la somme:

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

Une partie de la valeur dans le champ Nom contient une virgule et BREAK mon script awk. Ma question est: peut awk résoudre ce problème? Si oui, et comment puis-je faire?

Merci.

Était-ce utile?

La solution

vous écrivez une fonction awk comme ci-dessous:

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

vous pouvez intégrer dans votre script cette fonction et vérifier si le troisième champ est un champ numérique ou non numérique not.If alors allez 4ème et si le 4ème inturn sur le terrain n'est pas aller pour le 5 format: numérique ... jusqu'à ce que vous atteignez un numérique value.probably une boucle aider ici, et l'ajouter à la somme.

Autres conseils

Une façon utilisant GNU awk et FPAT

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

Résultat:

192

Vous êtes probablement mieux ce faire en Perl avec Text :: CSV, puisque c'est une solution rapide et robuste.

Vous pouvez aider awk travailler avec des champs de données contenant des virgules (ou) à l'aide de nouvelles lignes un petit script que j'ai écrit appelé csvquote. Il remplace les virgules offensants à l'intérieur des champs cités avec des caractères non imprimables. Si vous avez besoin, vous pouvez ensuite restaurer ces virgules -. Mais dans ce cas, vous n'avez pas besoin

Voici la commande:

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

voir https://github.com/dbro/csvquote le code

J'utilise

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

pour définir les champs avec gawk. J'ai trouvé que lorsque le champ est nul, cela ne reconnaît pas le nombre correct de champs. Parce que « + » nécessite au moins 1 caractère dans le champ. Je l'ai changé à:

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

et remplacer "+" avec "*". Il fonctionne correctement.

Je trouve aussi que le Guide de l'utilisateur GNU Awk a aussi ce problème. https://www.gnu.org/software/gawk /manual/html_node/Splitting-By-Content.html

Pour aussi simple un fichier d'entrée que vous pouvez simplement écrire une petite fonction pour convertir tous les réel les FS en dehors des guillemets pour une autre valeur (j'ai choisi RS depuis le séparateur d'enregistrement ne peut pas faire partie du dossier), puis utilisation en tant que le FS, par exemple:

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

Il ne se complique lorsque vous avez à traiter avec embarqués et intégrés des sauts de ligne citations dans les citations échappées et même alors il est pas trop dur et tout a été fait avant ...

Voir Quelle est la façon la plus robuste pour efficacement Parse CSV à l'aide awk? pour plus d'informations.

Vous pouvez toujours aborder le problème de la source. Mettez des guillemets autour du champ de nom, tout comme le champ de « Je suis la, question ». Cela est beaucoup plus facile que de passer vos solutions de contournement de codage de temps pour cela.

Mise à jour (comme Dennis a demandé). Un exemple 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

Comme vous pouvez le voir, en réglant le séparateur à guillemet, les champs qui appartiennent aux « guillemets » sont toujours nombre pair. Étant donné que l'OP n'a pas le luxe de modifier les données source, cette méthode ne sera pas approprié pour lui.

Si vous êtes sûr que la colonne « valeur » est toujours la dernière colonne:

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

NF représente le nombre de champs, donc $ NF est la dernière colonne

Cet article m'a aidé à résoudre ce même problème de champ de données. La plupart CSV mettre un devis autour des champs avec des espaces ou des virgules en leur sein. Ce bousille le champ comptent pour awk sauf si vous les filtrer.

Si vous avez besoin des données dans les champs qui contiennent les déchets, ce n'est pas pour vous. ghostdog74 a fourni la réponse, qui se vide ce champ, mais maintient le nombre total de champ à la fin, ce qui est essentiel pour maintenir la sortie de données cohérentes. Je n'ai pas aimé la façon dont cette solution a introduit de nouvelles lignes. Ceci est la version de cette solution je. Les trois champs de poing n'a jamais eu ce problème dans les données. Le quatrième champ contenant le nom du client souvent fait, mais je avais besoin que les données. Les autres champs qui présentent le problème que je pouvais jeter sans problème, car il n'a pas été nécessaire dans ma sortie du rapport. Donc, j'ai Sed les ordures du champ 4 très précisément et supprimer les deux premiers cas de citations. Ensuite, je demande ce que ghostdog74gave pour vider les autres champs qui ont des virgules dans les - cela supprime aussi les citations, mais j'utiliser printfto maintenir les données dans un seul enregistrement. Je commence avec 85 champs et se retrouvent avec 85 champs dans tous les cas de mes 8000+ lignes de données en désordre. Un score parfait!

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 solution qui vide les champs par des virgules dans les mais maintient aussi le dossier, bien sûr:

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

Megs de remerciement à ghostdog74 pour la grande solution!

NetsGuy256 /

FPAT est la solution élégante car il peut gérer les virgules redoutés dans problème des guillemets, mais pour résumer une colonne de chiffres dans la dernière colonne quel que soit le nombre de séparateurs précédent, $ NF fonctionne bien:

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

Pour accéder à la deuxième à la dernière colonne, utilisez ceci:

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

parseurs CSV à part entière comme le Text::CSV_XS de Perl sont spécialement conçu pour gérer ce genre de bizarreries.

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 est nécessaire puisque les données d'entrée contient des espaces entourant les séparateurs par des virgules. versions très anciennes de Text::CSV_XS ne supportent pas cette option.

J'ai fourni plus d'explications sur Text::CSV_XS dans mon réponse ici: Parse fichier csv en utilisant gawk

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top