30

awkを使用して、csvファイルの1つの列の合計をカウントしています。データ形式は次のようなものです。

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

私はこのawkスクリプトを使用して合計をカウントしていました:

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

名前フィールドの値の一部にコンマが含まれているため、awkスクリプトが壊れます。私の質問は:awkはこの問題を解決できますか?はいの場合、どうすればそれを行うことができますか?

ありがとうございました。

4

12 に答える 12

30

FPATGNU awkを使用する一方向

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

結果:

192
于 2012-10-18T14:26:19.107 に答える
7

使ってます

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

gawkでフィールドを定義します。フィールドがnullの場合、これは正しい数のフィールドを認識しないことがわかりました。「+」はフィールドに少なくとも1文字必要なためです。私はそれを次のように変更しました:

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

に置き換え"+"ます"*"。正しく動作します。

また、GNUAwkユーザーガイドにもこの問題があることがわかりました。 https://www.gnu.org/software/gawk/manual/html_node/Splitting-By-Content.html

于 2015-08-08T04:19:43.117 に答える
5

これは高速で堅牢なソリューションであるため、Text::CSVを使用してperlで実行する方がおそらく良いでしょう。

于 2010-06-30T17:32:27.140 に答える
4

単純な入力ファイルの場合、引用符の外側にあるすべての実際のFSを他の値に変換する小さな関数を記述して(レコード区切り文字をレコードの一部にすることはできないため、RSを選択しました)、それを次のように使用できます。 FS、例:

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

埋め込まれた改行と引用符内の埋め込まれたエスケープされた引用符を処理する必要がある場合にのみ複雑になりますが、それでもそれほど難しくはなく、すべて以前に行われています...

awkを使用してCSVを効率的に解析するための最も堅牢な方法は何ですか?を参照してください。詳細については。

于 2012-10-18T14:20:06.880 に答える
3

私が書いたcsvquoteという小さなスクリプトを使用すると、awkがコンマ(または改行)を含むデータフィールドを操作できるようになります。引用符で囲まれたフィールド内の問題のあるコンマを非印刷文字に置き換えます。必要に応じて、後でこれらのコンマを復元できますが、この場合は、復元する必要はありません。

コマンドは次のとおりです。

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

コードについてはhttps://github.com/dbro/csvquoteを参照してください

于 2013-05-04T21:08:15.387 に答える
2

あなたはいつでもソースから問題に取り組むことができます。「Iamthe、question」のフィールドと同じように、名前フィールドを引用符で囲みます。これは、そのための回避策のコーディングに時間を費やすよりもはるかに簡単です。

更新(デニスの要求に応じて)。簡単な例

$ 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

ご覧のとおり、区切り文字を二重引用符に設定すると、「引用符」に属するフィールドは常に偶数になります。OPにはソースデータを変更する余裕がないため、この方法は彼には適していません。

于 2010-06-29T07:19:20.557 に答える
2

この記事は、これと同じデータフィールドの問題を解決するのに役立ちました。ほとんどのCSVは、スペースまたはコンマを含むフィールドを引用符で囲みます。これは、フィルターで除外しない限り、awkのフィールド数を台無しにします。

ガベージを含むフィールド内のデータが必要な場合、これは適切ではありません。ghostdog74そのフィールドを空にしますが、最終的には合計フィールド数を維持するという答えを提供しました。これは、データ出力の一貫性を維持するための鍵です。このソリューションが新しいラインを導入する方法が気に入らなかった。これは、私が使用したこのソリューションのバージョンです。最初の3つのフィールドでは、データでこの問題が発生することはありませんでした。顧客名を含む4番目のフィールドはよくありましたが、そのデータが必要でした。問題を示す残りのフィールドは、レポート出力では必要なかったため、問題なく破棄できました。そこで、最初に4番目のフィールドのガベージを非常に具体的に削除し、引用符の最初の2つのインスタンスを削除しました。それから私は何を適用しますghostdog74カンマを含む残りのフィールドを空にするために与えられました-これも引用符を削除しますが、私printfはデータを単一のレコードに維持するために使用します。私は85フィールドから始めて、すべての場合で8000行以上の乱雑なデータから85フィールドで終わります。満点!

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

もちろん、フィールド内にコンマが含まれているフィールドを空にするだけでなく、レコードを維持するソリューションは次のとおりです。

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

素晴らしい解決策をくれたghostdog74に感謝します!

NetsGuy256 /

于 2012-10-18T00:35:20.837 に答える
2

FPATは、引用符内の恐ろしいコンマの問題を処理できるため、洗練されたソリューションですが、前の区切り文字の数に関係なく、最後の列の数値の列を合計するには、$NFが適切に機能します。

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

最後から2番目の列にアクセスするには、次を使用します。

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

于 2014-04-06T01:59:14.057 に答える
1

'value'列が常に最後の列であることが確実にわかっている場合:

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

NFはフィールドの数を表すため、$NFが最後の列になります

于 2010-06-30T17:44:43.483 に答える
1

Perlのような本格的なCSVパーサーText::CSV_XSは、そのような奇妙さを処理するために特別に作成されています。

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入力データにはコンマ区切り文字を囲む空白があるため、が必要です。の非常に古いバージョンは、Text::CSV_XSこのオプションをサポートしていない可能性があります。

Text::CSV_XS私はここで私の答え の中でより多くの説明を提供しました: gawkを使用してcsvファイルを解析します

于 2015-11-03T00:37:33.567 に答える
0

ファイルをperl正規表現にパイプして、引用符で囲まれたを|のような他のものに変換してみることができます。

cat test.csv | perl -p -e "s/(\".+?)(,)(.+?\")/\1\|\3/g" | awk -F, '{...

上記の正規表現は、二重引用符内に常にコンマがあることを前提としています。したがって、コンマをオプションにするには、さらに多くの作業が必要になります

于 2021-09-09T20:27:28.863 に答える
-6

以下のようにawkで関数を記述します。

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

スクリプトにこの関数を組み込んで、3番目のフィールドが数値かどうかを確認できます。数値でない場合は4番目のフィールドに進み、4番目のフィールドが数値でない場合は5番目に進みます...数値に達するまで。おそらくループがここで役立ち、それを合計に追加します。

于 2010-06-30T17:14:21.590 に答える