CSVファイルを最初の列(ヘッダーを含む)で分割する方法は?

CSVファイルを最初の列(ヘッダーを含む)で分割する方法は?

これは他の2つの質問の組み合わせです(1行にプレフィックスでファイルを分割する方法そしてヘッダーを含む列に基づいてファイルを分割する方法)。私は次のように始めたいと思いますinput.csv

id,first,second,third
1,a,b,c
333,b,b,b
1,d,e,f
2,d,e,f
1,c,d,e
333,a,a,a
[more lines in the same format]

この内容に1.csv

id,first,second,third
1,a,b,c
1,d,e,f
1,c,d,e

、このコンテンツは次の場所にあります2.csv

id,first,second,third
2,d,e,f

、この333.csv

id,first,second,third
333,b,b,b
333,a,a,a

、それは:

  1. すべてのIDを次に変更してください。窒素入力するN.csv
  2. 行を順番に保持オリジナルのように。
  3. タイトルを含めるすべての出力ファイルの生ファイル。

これも非常に速くなければならないので、while readループは次のようになります。いいえ切ってしまいます。

答え1

この GNU awk コマンドはこの問題を解決できます。

awk -F ',' 'NR==1{h=$0; next};!seen[$1]++{f=$1".csv"; print h > f};{f=$1".csv"; print >> f; close(f)}' input.csv

警告:最初のフィールドにエスケープされたカンマがあると機能しません。他の分野のカンマは正常に動作します。

説明する:

  • -F ','(フィールド区切り文字)$1などがスペースで区切られた値ではなくCSV列を参照していることを確認してください。
  • NR==1{h=$0; next}NR==1ヘッダー行全体を変数h()に保存し、その行()をスキップしてh=$0最初の行()を特別に処理しますnext
  • !seen[$1]++{f=$1".csv"; print h > f}$1特殊文字()が最初に発生した場合は、次の項目をfilename変数に保存し、ヘッダーを対応するファイル()に保存して処理します。!seen[$1]$1.csvfprint h > f
  • {f=$1".csv"; print >> f; close(f)}現在の行をファイルに追加し()、print >> fファイル記述子を閉じて(close(f))、特定のIDを持つすべての行が処理された後にその行が保持されないようにします。

ボーナス:別のフィールドに置き換えると、期待$1どおりに機能するはずです。つまり、特定の列にその値を含む行を含むその列の各固有値のファイルを生成する必要があります。

答え2

(他の回答ですべての人にスパムを送信して申し訳ありません。)多くの状況で提供されるエレガントなawkバージョンは完璧です。しかし、気の利いた言葉以上の人生があり、私たちには多くのことが必要です。

  • 複雑なcsvファイルを処理する追加のコードを追加します。
  • 追加の正規化、フォーマットの上書き、処理ステップを追加します。

次のフレームワークはCSVファイルのパーサーを使用します。今回は整数の使用を避け、さらに変数を厳密に宣言しました!

#!/usr/bin/perl

use strict;
use Parse::CSV;
my %dict=();

my $c = Parse::CSV->new(file => 'a1.csv');

while ( my $row = $c->fetch ) {                    ## for all records
   $dict{$row->[0]} .=   join(" :: ",@$row)."\n";  ## process and save
}

for my $k (keys %dict){                            ## create the cvs files
   open(F,">","$k.cvs") or die;
   print F $dict{$k};
   close F;
}
  • 最大の利点は、より複雑なcsvファイルを処理できることです。今回は、csv入力に ";"を含む文字列を含めることができ、複数行フィールドを含めることができます(csv仕様は複雑です!)。
 1111,2,3
 "3,3,3",a,"b, c, and d"
 "a more, complex
        multiline record",3,4
  • 処理ステップを説明するために、フィールド区切り文字を「:」に変更した。
  • 追加の手順を説明するために、いくつかの最適化を追加しました。事前キャッシュを使用したため、このスクリプトは他のソリューションよりも100倍速く実行されました。

答え3

これは答えではなく、IObOの優れた答えのスクロール防止バリエーションです...

awk -F, 'NR==1{h=$0; next} {print seen[$1]++ ? $0 : h "\n" $0 >$1 ".csv"}'

答え4

パイプのみを使用し、次を使用しない古いバージョンawk

警告する:上記の解決策より平均的に遅く実行され、awk速度は入力ファイルのキー数によって異なります。

cut -d , -f 1 input.csv | fgrep -v id | sort | uniq | xargs -n1 sh -c '(head -n1 input.csv && egrep "^${0}," input.csv) > ${0}.csv'

それがすることは:

  • cut -d , -f 1 input.csvファイルの各行を文字で分割し、,最初の列(-f 1)を取得してキーのみを保持します。
  • fgrep -v idタイトルをスキップ
  • sort | uniq各キーの1つだけを並べ替えて保管してください。
  • xargs -n1 sh -c '<sub shell>'各キーに対してサブシェルを実行する
  • head -n1 input.csvサブシェルの最初の部分は入力ファイルのヘッダーを取得します。
  • その後、egrep "^${0}," input.csvキーに一致する行をつかみ、明確ではないかもしれませんが、これは行ごとに繰り返されるため、遅くなります。
  • 最後に、> ${0}.csv出力はキー名のファイルに書き込まれます。

関連情報