web-dev-qa-db-ja.com

CSVファイルの文字列値のコンマの処理

数値と文字列の列があるコンマ区切りファイルがあります。文字列の列は引用符で囲まれ、引用符の間にコンマを含めることができます。列をFS =","で識別するにはどうすればよいですか?

サンプルレコード

"prabhat,kumar",19,2000,"bangalore,India"

[〜#〜] awk [〜#〜] では、

$1 = "prabhat,kumar"
$2 = 19
$3 = "2000"
$4 = "bangalore,india"

FS=","を設定すると問題が発生します。

入力は:

"prabhat,kumar",19,2000,"bangalore,India","ABC,DEF","GHI",123,"KLM","NOP,QRS"
"prabhat,kumar",19,2000,"bangalore,India","ABC,DEF","GHI",123,"KLM","NOP,QRS"

出力は次のようになります。

"prabhat,kumar"|19|2000|"bangalore,India"|"ABC,DEF"|"GHI"|123|"KLM"|"NOP,QRS"
"prabhat,kumar"|19|2000|"bangalore,India"|"ABC,DEF"|"GHI"|123|"KLM"|"NOP,QRS"

私が試しているコード:

awk -F"," '{for(i=1;i<=NF;i++){if(i%NF==0){ORS="\n"} {if($i ~ /^\"/ || $i ~ /\"$/) {a=a OFS $i;j++;{if(j%2==0){sub(/^\,/,X,a); print a;j=0;a=""}}} else {print $i}}} {ORS="|"}}' ORS="|" OFS=, p.txt
4
prabhat diwaker

まず、適切なCSVパーサーを使用する必要があります。たとえば、PerlではText::CSVを使用できます。

  1. cpanmをインストールします(Perlを使用している場合は、後で私に感謝します)

    $ Sudo apt-get install cpanminus
    

    Debianベースのシステムを使用していない場合は、ディストリビューションのパッケージマネージャーを使用してインストールできるはずです。

  2. Text::CSVモジュールをインストールする

    $ Sudo cpanm Text::CSV
    
  3. ファイルを解析する

    $ Perl -MText::CSV -le '
        $csv = Text::CSV->new({binary=>1}); 
        while ($row = $csv->getline(STDIN)){
        print "1:$row->[0], 2:$row->[1], 3:$row->[2], 4:$row->[3]"}' < file.csv 
    1:prabhat,kumar, 2:19, 3:2000, 4:bangalore,India
    

    上記のように、最初のフィールドは$row->[0]、2番目のフィールドは$row->[1]などです。


それは正しい方法でした。より単純ですが、汚いハックは、引用符で囲まれたコンマを別の文字に置き換えることです。次に、通常はawkを使用し、最後にそれらを再びコンマに戻します。ここでは###を使用していますが、自分のフィールドの1つに含まれないことが確実なものであれば何でも使用できます。

$ sed -r 's/("[^",]+),([^",]+")/\1###\2/g' file.csv | 
    awk -F, '{print $1,$3}' | sed 's/###/,/g'
"prabhat,kumar" 2000
5
terdon

GNU awkの場合:

_$ awk -vFPAT='[^,]*|"[^"]*"' '{ gsub("^\"|\"$","",$1); gsub("^\"|\"$","",$4); print $1 $4} '
prabhat,kumarbangalore,India
_

_$1_と_$4_を並べて印刷しただけなので、出力形式は少し見苦しいです。好みに合わせて変更できると思います。

フィールドを囲む二重引用符を保持する必要がある場合は、両方のgsub();関数を削除します。

説明:

通常、awkはレコードのフィールドをFS(フィールドセパレーター)変数の内容で区切ります。変数はデフォルトで空白(タブ、スペース、改行)になります。セパレータは、awkにレコードの終了位置を伝えます。 csvファイルでは、レコードはコンマで終わります(awkに_-vFS=,_として渡されます)が、もちろん、あなたの例と同様の例では、これは単純すぎて壊れます。

または、FPAT(フィールドパターン)はawk内のレコードを定義します。 awkにレコードの終了位置を通知する代わりに、レコード全体を含む定義を作成します。あなたの例の複雑なcsvでは、これは_[^,]*|"[^"]*"_です

これは次のように分類されます。

  • カンマ以外の文字(_[^,]_)をできるだけ多く(_*_)。 2つのコンマの間のすべてがフィールドです。
  • または(_|_)
  • 単一の二重引用符(_"_)に続く二重引用符なし(_[^"]_)可能な限り多くの回数(_*_)に続く単一の二重引用符(_"_ )。二重引用符内のすべて(コンマを含む)は1つのフィールドとしてカウントされます。
4
garethTheRed

RubyはCSV解析に便利です。

Ruby -rcsv -ne 'puts CSV.generate_line(CSV.parse_line($_), :col_sep=>"|")' file
prabhat,kumar|19|2000|bangalore,India|ABC,DEF|GHI|123|KLM|NOP,QRS
prabhat,kumar|19|2000|bangalore,India|ABC,DEF|GHI|123|KLM|NOP,QRS

出力には引用符がないことに注意してください。これは、どのフィールドにもフィールドセパレータが含まれていないためです。引用符が必要な場合は、すべてのフィールド(整数も含む)を強制的に引用できます。

Ruby -rcsv -ne 'puts CSV.generate_line(CSV.parse_line($_), :col_sep=>"|",:force_quotes=>true)' file
"prabhat,kumar"|"19"|"2000"|"bangalore,India"|"ABC,DEF"|"GHI"|"123"|"KLM"|"NOP,QRS"
"prabhat,kumar"|"19"|"2000"|"bangalore,India"|"ABC,DEF"|"GHI"|"123"|"KLM"|"NOP,QRS"
1
glenn jackman

おそらくqsvの方があなたの速度でしょうか?

sed 's/.*/,&,/;:t
s/,"/"/;s/",/"/;s/,\([^"]*\),/"\1/;tt
' <<\DATA
"prabhat,kumar",19,2000,"bangalore,India"
DATA

出力

"prabhat,kumar"19"2000"bangalore,India"
0
mikeserv

代わりにPerlを使用できます。

$ echo '"prabhat,kumar",19,2000,"bangalore,India"' |
  Perl -F',(?![[:alpha:]])' -anle 'print "$F[0] $F[3]"'
"prabhat,kumar" "bangalore,India"
0
cuonglm

これは私のために働きました:

$ echo '"prabhat,kumar",19,2000,"bangalore,India"' | 
  awk -F, '{print $1,$2,$3,$4,$5,$6}'| 
    awk -F\" '{print $2,$3,$4}'|awk -F\  '{print $1","$2,$3,$4,$5","$6}'`
0
Andrew