Perl入門ゼミ

テキスト処理、Linuxサーバー管理、Web開発ならPerl
  1. Perl
  2. ファイル入出力
  3. here

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 (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関数で分割したリストを代入します。

  • Perlとはテキスト処理の記述性とパフォーマンスに優れ、正規表現が言語に組み込まれているプログラミング言語です。
  • Linuxサーバーでのフィルタリングプログラム、複数行の文字列を処理、ファイル内容の検索・置換などが得意
  • Perlはgitopensslなど広く普及したUnix/Linuxミドルウェアの補助ツールとして採用実績あり。後方互換性とポータビリティの高さがひとつの理由と推測。
  • 大量のテキストを扱うWeb開発も得意。ロングテールSEOを意識したWebサイト、アドテクやソーシャルゲームでの50ms以内のJSONの生成など。