先週からの続きです
先週でテキストファイルの読み書きができるようになりました。
ということで、今回は代表的なテキストファイルのデータとしてCSVの処理方法をやっていきます。
CSVファイル
Comma Separated Values の頭文字を取った名前の通り、カンマ区切のデータのことです。
1行にカンマ区切りで複数のデータのがあり、それが複数行ある・・・というです。
この1行のことをレコードと呼び、レコードの中のカンマで区切られたところをフィールドと呼びます。
Perl入学式のリファレンス回の練習問題からデータを持ってcsv風に書くとこんな感じです。
name,country,perl,python,ruby,php,binary Alice,England,60,80,80,50,30 Bob,America,40,10,50,30,50
では、これを records.csv というテキストファイルにして、色々と処理をしていきます。
csvファイルを単に読むだけ
先週の復習です。csvファイルと今回作成するPerlのスクリプトは同じ場所に置いて実行してください。
#!/usr/bin/env perl use strict; use warnings; my $filename = 'records.csv'; open my $FH, '<', $filename; for my $line (<$FH>){ chomp $line; print $line . "\n"; } close $FH;
csvを読み込んで配列にする
データが詰まっている1行はカンマ区切りなので、split
で分割して配列に入れるだけです。
#!/usr/bin/env perl use strict; use warnings; my $filename = 'records.csv'; open my $FH, '<', $filename; for my $line (<$FH>) { chomp $line; my @field = split /,/, $line; # カンマ区切りで配列に格納 print "@field" . "\n"; # 配列をprint } close $FH;
ファイルの読み込みと、読み込んだデータの処理を分けて書くときとには配列のリファレンスを使うことになると思います。
こんな感じかなー。無名配列でもっと短くかけますが、そこは各自で・・・
#!/usr/bin/env perl use strict; use warnings; my @records; # 配列リファレンスを格納する配列 my $filename = 'records.csv'; open my $FH, '<', $filename; for my $line (<$FH>) { chomp $line; my @field = split /,/, $line; # カンマ区切りで配列に格納 my $record = \@field; # 配列リファレンスにする push @records, $record; # 配列リファレンスを格納用の配列に入れる } close $FH; # 結果表示 for my $record (@records) { my @record = @{$record}; # デリファレンスして配列に戻す print "@record" . "\n"; # デリファレンスしたものを表示 }
csvを読み込んで配列にする(モジュールを使う)
ここまで書いてきてなんですが、自分でパース(データを分けること)することはお勧めしません。
例えば、フィールドのデータ内に ,
があると、split
はそこで分割してしまいます。
Perlでcsvを扱うモジュールといえば Text::CSV_XS
が定番です。Text::CSV_XS
であれば、そのようなデータにもうまく対応してくれます。
(ただし、処理するファイルの前提はある)
また、スクリプトの上の方に use Text::CSV_XS;
とあると、「あー、csvを何かするスクリプトなのね」と理解の一助になります。
なお、標準モジュールではないのでインストールが必要です。
$ cpanm Text::CSV_XS
利用例としてはこんな感じです。
#!/usr/bin/env perl use strict; use warnings; use TEXT::CSV_XS; my $csv = Text::CSV_XS->new(); # csvを扱う便利オブジェクト my $filename = 'records.csv'; open my $FH, '<', $filename; for my $line (<$FH>) { chomp $line; my $status = $csv->parse($line); # CSV文字列をパースしてフィールド群に切り分ける # $status は成否判定が入っている(今回は使わない) my @columns = $csv->fields(); # パースされたフィールド群を配列に入れる print "@columns" . "\n"; } close $FH;
csvを読み込んでハッシュにする
こちらの使い方も多いと思います。
ハッシュにするにあたって考えるのは、key
と value
をどうするか?ということです。
今回はこんな感じのデータ構造を作ってみます。配列の中にハッシュリファレンスが入っています。
ハッシュなので順不同なのですが、見やすさ優先で key
でソートしてます。
$VAR1 = [ { 'binary' => '30', 'country' => 'England' 'name' => 'Alice', 'perl' => '60', 'python' => '50', 'ruby' => '80', }, { 'binary' => '50' 'country' => 'America', 'name' => 'Bob', 'perl' => '40', 'python' => '30', 'ruby' => '50', } ];
#!/usr/bin/env perl use strict; use warnings; use Data::Dumper; my @records; # ハッシュリファレンスを格納する配列 my $filename = 'records.csv'; open my $FH, '<', $filename; for my $line (<$FH>) { chomp $line; next if $line =~ /^name/; # nameから始まる項目名の行だったら飛ばす my @field = split /,/, $line; # カンマ区切りで配列に格納 # ハッシュのデータ構造にする my %record = ( name => $field[0], country => $field[1], perl => $field[2], python => $field[3], ruby => $field[4], python => $field[5], binary => $field[6], ); my $record = \%record; # ハッシュリファレンスにする push @records, $record; # ハッシュリファレンスを格納用の配列に入れる } close $FH; print Dumper \@records;
csvを読み込んでハッシュにする(モジュールを使う)
ここまで長く書いてきてなんですが、ハッシュにするときもモジュールを使うのが楽でおすすめです。
こちらも標準モジュールではないのでインストールが必要です。
$ cpanm Text::CSV::Simple
3行で収まっちゃった・・・
#!/usr/bin/env perl use strict; use warnings; use Data::Dumper; use Text::CSV::Simple; my $filename = 'records.csv'; my $parser = Text::CSV::Simple->new; $parser->field_map(qw/name country perl python ruby php binary/); my @data = $parser->read_file($filename); print Dumper \@data;
というわけで
駆け足ながらPerlでのcsvファイル処理について書いてみました。
誰向けの記事かというと、6年くらい前の自分向けの記事です。