awkは引用されたフィールド内にコンマを含むCSVファイルを扱うことができますか?

StackOverflow https://stackoverflow.com/questions/3138363

質問

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はこの問題を解決できますか?はいの場合、どうすればそれを行うことができますか?

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

役に立ちましたか?

解決

以下のようにawkで関数を書きます:

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

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

他のヒント

1つの方法 GNU awkfpat

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

結果:

192

おそらく、テキスト:: CSVでPerlでそれを行う方が良いでしょう。なぜなら、それは高速で堅牢な解決策だからです。

CSVQuoteと呼ばれる小さなスクリプトを使用して、コンマ(またはニューライン)を含むデータフィールドを使用するのを支援できます。引用されたフィールド内の問題のあるコンマを非印刷文字に置き換えます。必要な場合は、後でそれらのコンマを復元することができますが、この場合はそうする必要はありません。

これがコマンドです:

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

見る https://github.com/dbro/csvquote コード用

使ってます

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

ゴークでフィールドを定義する。フィールドがヌルの場合、これは正しい数のフィールドを認識しないことがわかりました。 「+」には、フィールドに少なくとも1つの文字が必要だからです。私はそれを変更しました:

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

交換します "+""*". 。正しく機能します。

また、GNU AWWユーザーガイドにもこの問題があることがわかります。https://www.gnu.org/software/gawk/manual/html_node/splitting-by-content.html

簡単な入力ファイルと同様に、引用符以外のすべての実際のFSSを他の値に変換するために小さな関数を記述できるように(レコードセパレーターがレコードの一部になることができないため、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を効率的に解析する最も堅牢な方法は何ですか? 詳細については。

ソースから問題にいつでも取り組むことができます。 「私は、質問」のフィールドと同じように、名前フィールドの周りに引用を置きます。これは、そのために仕事のコーディングに時間を費やすよりもはるかに簡単です。

アップデート(デニスが要求したように)。簡単な例

$ 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

ご覧のとおり、区切り文字を2倍の引用に設定することにより、「引用」に属するフィールドは常に均等な数字です。 OPにはソースデータを変更する贅沢がないため、この方法は彼にとって適切ではありません。

「値」列が常に最後の列であることを確実に知っている場合:

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

NFはフィールドの数を表すため、$ nfが最後の列です

この記事は、この同じデータフィールドの問題を解決するのに役立ちました。ほとんどの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/

FPATは、引用符の問題の中で恐ろしいコンマを処理できるため、エレガントなソリューションですが、先行するセパレーターの数に関係なく、最後の列の数字の列を合計するために、$ nfはうまく機能します。

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

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

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

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ファイルを解析します

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top