csvファイルを読み込んでPerlのハッシュの配列に変換する
csv形式のファイルを読み込んで、ハッシュの配列に変換するサンプルです。
ハッシュスライスという機能を積極的に使っているので、可読性はちょっと低いです。Perlをしばらく使って慣れていないと暗号に見えるかもしれません。前回のサンプルとほぼ構造は同じなので、違う部分だけを解説します。
use strict; use warnings; # 引数の処理 my $file = shift; unless ($file) { die "Usage: $0 file"; # 引数がないときは、使用方法を示して終了。 } # csvの各列に対応するキーを作成 my $headers = [ 'name', 'age', 'country' ]; # ファイルを解析してcsv形式のデータを配列の配列に変換。 # ヘッダのキーも引数として渡す。 my @recs = parse_file($file, $headers); # 出力( ハッシュの配列なので、forでたどる ) for my $items ( @recs ){ # ハッシュスライスを利用して連結 print join(',', @{$items}{@$headers} ) . "\n"; } # ファイル解析用の関数( 今回は単に書き戻すだけだけれど・・ ) sub parse_file { my ($file, $headers) = @_; open(my $fh, "<", $file) or die "Cannot open $file for read: $!"; # 複数のレコードを格納する配列へのリファレンスを準備 my $recs = []; while (my $line = <$fh>) { # 改行を取り除く chomp $line; # データを格納するハッシュへのリファレンスを準備 # ハッシュスライスを利用してヘッダ # に対応するキーに代入 my $items = {}; @{$items}{@$headers} = split(',', $line); # push関数の第1引数は配列なので、@$recsとデリファレンス。 push @$recs, $items; } close $fh; wantarray ? return @$recs : return $recs; }
以下は、csvデータのサンプルです。csvファイルを作って、スクリプトの第1引数に与えて実行してください。
masao,10,Japan taro,20,USA rika,38,France
コード解説
(1)ハッシュのキーの配列の作成
my $headers = ['name', 'age', 'country'];
ハッシュのキーを使うことで、前回は単に配列であったものに意味をつけて挙げます。csvファイルの一項目目はnameに、2項目目はageに、3項目目はcountory に対応します。
(2)ファイルを読み込んで、csv形式のテキストをハッシュの配列に変換する
my @recs = parse_file($file, $headers);
parse_fileは、ファイルを読み込んで、csv形式のテキストをハッシュの配列に変換する自作の関数です。入力と出力のイメージは以下のようになります。
masao,10,Japan taro,20,USA rika,38,France ↓ @recs = ( { name => 'masao', age => 10, country => 'Japan' }, { name => 'taro', age => 20, country => 'USA' }, { name => 'rika', age => 38, country => 'France' }, )
(3)ハッシュの配列を出力する
for my $items (@recs) { print join(',', @{$items}{@$headers}) . "\n"; }
配列なので外側はforeachループになります。ハッシュへのリファレンスである$itemsをjoin関数でカンマで連結して出力します。
(3)-1 ハッシュスライスの解説
難解なハッシュスライス @{$items}{@$headers} を解説します。
まず $items はハッシュへのリファレンスで。
{ name => 'masao', age => 10, country => 'Japan' }
のようになっています。
デリファレンスするには普通 %$items としますが、ハッシュスライスを利用できるようにデリファレンスしたいので
@{$items}{ ハッシュのキーのリスト }
とします。
@{$items}{'name', 'age', 'country'}
と書くと
('masao', 10, 'Japan')
が取得できます。
また、$headers には、['name', 'age', 'country'] が代入されていますので、@$headers とデリファレンスすると('name', 'age', 'country') が取得できます。
@{$items}{@$headers}
という記述は、
@{$items}{'name', 'age', 'country'}
という記述と同じになり、
('masao', 10, 'Japan')
が取得できます。
join(',', @{$items}{@$headers})
として、カンマで結びつけて完成です。
(4)whileループでの処理
while文を使って、Perlのデータ構造を作成しています。
while (my $line = <$fh>) { # 改行を取り除く chomp $line; # データを格納するハッシュへのリファレンスを準備 my $items = {}; # ハッシュスライスを利用してヘッダ # に対応するキーに代入 @{$items}{@$headers} = split( ',', $line ); # push関数の第1引数は配列なので、@$recs # とデリファレンス。 push @$recs, $items; }
(4)-1 ハッシュへのリファレンスを用意
my $items = {}; # データを格納するハッシュへのリファレンスを準備
配列の中に持たせるハッシュへのリファレンスを準備します。
(4)-2 ハッシュスライスを左辺において対応するキーに代入
@{$items}{@$headers} = split(',', $line);
上記で解説したことと同じです。左辺をハッシュスライスにして、そこにsplit関数で分割したリストを代入します。