CSVファイルの列データを結合するperl script


実験データのCSVファイルの列を連結する作業をお願いされたので、perl scriptをつくりましたので、おすそ分けします。

### CSVファイルの形式
ファイル名　foo_range5to9_test.csv

||5|6|7|8|9|
|:--|:--|:--|:--|:--|:--|
|1|.|.|.|.|.|.|
|2|.|.|.|.|.|.|
|3|.|.|.|.|.|.|
|...|.|.|.|.|.|.|

ファイル名　foo_range10to14_test.csv

||10|11|12|13|14|
|:--|:--|:--|:--|:--|:--|
|1|*|*|*|*|*|
|2|*|*|*|*|*|
|3|*|*|*|*|*|
|...|*|*|*|*|*|

これが、フォルダー(SRC)に入っている
foo_range5to9_test.csv 
foo_range10to14_test.csv
bar_range0to4_test.csv 
bar_range5to9_test.csv

### 結合結果
outフォルダーにまとめたファイルができる
foo_range5to14_test.csv 
bar_range0to9_test.csv 

||5|6|7|8|9|10|11|12|13|14|
|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|
|1|.|.|.|.|.|*|*|*|*|*|
|2|.|.|.|.|.|*|*|*|*|*|
|3|.|.|.|.|.|*|*|*|*|*|
|...|.|.|.|.|.|*|*|*|*|*|

###スクリプト
```使い方
% perl スクリプト.pl SRC/*
```

```perl:CSVファイルの列結合
# CSVファイルの列結合perl script
#
# copyright 2018 0x20FE

#結合したいファイル名を引数として呼ぶ
#  例　perl merge.pl SRC/foo_range10to14_test.csv SRC/foo_range5to9_test.csv SRC/bar_range0to4_test.csv SRC/bar_range5to9_test.csv
# このときは out/foo_range0to9_test.csvとout/var_range0to9_test.csv を出力する
# ファイル名は、(ディレクトリ名)/(プレフィックス)_範囲識別名(数字)to(数字）_(ポストフィックス）.csv

$PARKEY='range';
@IN=@ARGV;

& fn2grp(@IN);
#----------------------------------
sub fn2grp{
	my @files=@_;
    %FN=();# ファイル名
	foreach $fn (@files){
        ($d,$hd,$st,$en,$tl)=( $fn =~ m@^([^/]*)/(.*)_$PARKEY(¥d*)to(¥d*)_(.*).csv$@) or next;
#		print "$hd-$tl-$st-$en¥n";
        # まとめるグループ名を作成する
		$grp="$hd,$tl";
        $FN{$grp}=$FN{$grp}.",$fn"; # %FNにグループ名をキーとしてファイル名をカンマでつなげた文字データを作成する
        # 範囲の最小と最大を記録する
		$ST{$grp}=exists($ST{$grp})?($ST{$grp}<$st?$ST{$grp}:$st):$st;
		$EN{$grp}=$EN{$grp}>$en?$EN{$grp}:$en;
        # 各ファイルの開始列データを保持する
		$SVal{$grp}=$SVal{$grp}.",".$st;
	}

    ############################################
    # (i) %FN 列結合するCSVファイル名を持つ連想配列
    # (o) 列結合したCSVデータ・ファイル
    ############################################
	foreach $grp (keys %FN){
#		print "$grp : $ST{$grp}-$EN{$grp}¥n";
#		print "$grp : $FN{$grp}¥n";

		#--- gen group name ---
        # グループ名を作成する　　元のファイル名に書かれていた、範囲数の最小と最大を使用
		$st= $ST{$grp};$en=$EN{$grp};
		($hd,$tl)=split(/,/,$grp);
		$gnam=$hd."_$PARKEY".$st."to".$en."_".$tl;
		print "### $gnam ###¥n";
#		print "+++ $FN{$grp}¥n";
#		print "*** $SVal{$grp}¥n";

        #--- get filenames ----
        #ファイル名、開始番号のリストを取り出す
		@fns=split(/,/,$FN{$grp});
		shift(@fns);# drop dummy
		@svs=split(/,/,$SVal{$grp});
		shift(@svs);# drop dummy
        
        #========================================================
        #リストに記載された順が数字の大小と一致しないことがあるのでソートする
        #========================================================
        # SRC/foo_range10to14_test.csv SRC/foo_range5to9_test.csv なら
        # まず連想配列の開始番号とファイル名を対応づける
        #  $fnmap{10} => SRC/foo_range10to14_test.csv
        #  $fnmap{5} => SRC/foo_range5to9_test.csv
		undef %fnmap;
		for($i=0;$i<=$#svs;$i++){
			$fnmap{$svs[$i]}=$fns[$i];
		}
        #次に開始番号をソートして、キーを作成する
        #  5 , 10
        #  これをキーととしてファイル名を取り出すことで、ソートが完了する
		@fns_sort=();
		for $sv (sort {$a <=> $b} keys %fnmap){
			print $sv ," - ", $fnmap{$sv} ,"¥n";
			push(@fns_sort,$fnmap{$sv});
		}
#		next;
		@fns = @fns_sort;
        #========================================================

        ########################################
        #  結合処理
        #  ファイル名のリストを受け取り、２列目以降の列データを先頭ファイルのデータに追加する
        #
        # (i) @fns  ファイル名のリスト
        # (o) @TOP
        ########################################
        # 最初のファイルを読み込み、改行コードを削除する
		$top=shift(@fns);
		undef @TOP;
		open(FIN,$top);@TOP=<FIN>;	close(FIN);
		chop(@TOP);
		chop(@TOP);
        ########################################
        # のこりのファイルを読み込み、列に追加する
		foreach $fn (@fns){
            # ファイルを読み込み、改行コードを削除する
			print "----$fn---¥n";
			undef @IN;
			open(FIN,$fn);@IN=<FIN>;	close(FIN);
			chop(@IN);
			chop(@IN);
            # 読み込んだファイルの行数が、最初のファイルの行数と一致していることを確認する
			#print "$#TOP,$#IN¥n";
			($#TOP == $#IN) or die "FILE LENGTH inconstent! :$fn";
            #読み込んだファイルの各行に対して、先頭列のデータを捨て、先頭ファイルのデータに列データを追加する
			for($i=0;$i<=$#TOP;$i++){
				@in =  split(/,/,$IN[$i]);shift(@in); # drop 1st column
				$in = join(',',@in);
				$TOP[$i] .= " , " . $in;
				#print $TOP[$i]," , " . $IN[$i],"¥n";
				#print $TOP[$i],"¥n";
				#last if $i==2;
			}
		}
		## 末尾に改行記号を追加して出力する　cr/lf
		open(FOUT,">out/$gnam".".csv") or die;
		print FOUT "必要に応じてヘッダを追加するならここにいれる¥r¥n";
		for($i=0;$i<=$#TOP;$i++){
			print FOUT $TOP[$i] ,"¥r¥n";
		}
		close(FOUT);
	}
}
```