web-dev-qa-db-ja.com

複数の.csvファイルの内容を単一の.csvファイルにマージする

複数の.csvファイルの内容を1つの.csvファイルにマージするスクリプトを記述したい。つまり、他のすべてのファイルの列を最初のファイルの列に追加する。 「for」ループを使用してそうしようとしましたが、続行できませんでした。

Linuxでこれを行う方法を知っている人はいますか?

5
rmb

コマンドラインで指定された各ファイルの各行を読み取り、配列(@csv)の要素に追加するPerlスクリプトを次に示します。入力がなくなると、@csvの各要素が出力されます。

.csvファイルは、コマンドラインにリストされている順に追加されます。

[〜#〜] warning [〜#〜]:このスクリプトはすべての入力ファイルの行数が同じであると想定しています。ファイルの行数が他のファイルと異なる場合、出力はおそらく使用できなくなります。

#!/usr/bin/Perl

use strict;

my @csv=();

foreach (@ARGV) {
  my $linenum=0;

  open(F,"<",$_) or die "couldn't open $_ for read: $!\n";

  while (<F>) {
    chomp;
    $csv[$linenum++] .= "," . $_;
  };

  close(F);
};

foreach (@csv) {
  s/^,//;   # strip leading comma from line
  print $_,"\n";
};

次の入力ファイルがあるとします。

==> 1.csv <==
1,2,3,4
1,2,3,4
1,2,3,4
1,2,3,4

==> 2.csv <==
5,6,7,8
5,6,7,8
5,6,7,8
5,6,7,8

==> 3.csv <==
9,10,11,12
9,10,11,12
9,10,11,12
9,10,11,12

次の出力が生成されます。

$ ./mergecsv.pl *.csv
1,2,3,4,5,6,7,8,9,10,11,12
1,2,3,4,5,6,7,8,9,10,11,12
1,2,3,4,5,6,7,8,9,10,11,12
1,2,3,4,5,6,7,8,9,10,11,12

ここまで読んだので、paste -d, *.csvが実行しないことは何も実行しないことを認めましょう。では、なぜperlに悩むのでしょうか。 pasteは非常に柔軟性がありません。データがpasteの動作に完全に一致している場合は問題ありません。これは、作業に最適で非常に高速です。そうでなければ、それはあなたにとって完全に役に立たない。

このようなPerlスクリプトを改善する方法はいくつもあります(たとえば、各ファイルのフィールド数をカウントし、各ファイルの@csvに空のフィールドの正しい数を追加することにより、異なる長さのファイルを処理します( s)行が欠落している、または少なくとも異なる長さを検出してエラーで終了する)が、より高度なマージが必要な場合、これは妥当な出発点です。

ところで、これは非常に単純なアルゴリズムを使用し、すべての入力ファイルの内容全体をメモリ(@csv内)に一度に格納します。最新のシステムでそれぞれ数MBまでのファイルの場合、それは不当なことではありません。ただし、巨大な.csvファイルを処理している場合、より良いアルゴリズムは次のようになります。

  • すべての入力ファイルを開き、読み取る入力がまだある間:
    • 各ファイルから行を読み取る
    • 行を追加します(@ARGV順)
    • 追加された行を印刷する
3
cas

それを達成するための最も簡単なアプローチは、次のコマンドを入力することです

cat *csv > combined.csv

このファイルには、あなたが言及した方法で、すべてのcsvファイルの内容が含まれます。

13
zsha
awk '(NR == 1) || (FNR > 1)' *.csv > 1000Plus5years_companies_data.csv
3
user387763