PerlでCSVの読み書きを学ぶための自作ドリル

CSVを扱うと同時に、パッケージモジュールの扱いにも慣れさせるための問題と解答例。
モジュールはText::CSV_XSを使用。

問題

CSV書き出しスクリプト

name, email, addressを入力して、CSVでファイルに書き出す。(項目名はプロンプトとして表示する。)
実行結果

$ perl output_csv.pl
name : Taro Yamada(改行)
email : taro.yamada@xxx.com(改行)
address : tokyo(改行)
name : Jiro Tanaka(改行)
email : jiro.tanaka@xxx.com(改行)
address : osaka(改行)
name : Saburo Abe(改行)
email : saburo.abe@xxx.com(改行)
address : nagoya(改行)
name : (Ctrl+Dで終了)
$

書き出し結果ファイル(output_xxxxxxxxxx.csv)

name,email,address
"Taro Yamada",taro.yamada@xxx.com,tokyo
"Jiro Tanaka",jiro.tanaka@xxx.com,osaka
"Saburo Abe",saburo.abe@xxx.com,nagoya
CSV読み込みスクリプト

CSVファイルを読み込み、マップ形式で画面に表示する。
読み込みファイル(output_xxxxxxxxxx.csv)

name,email,address
"Taro Yamada",taro.yamada@xxx.com,tokyo
"Jiro Tanaka",jiro.tanaka@xxx.com,osaka
"Saburo Abe",saburo.abe@xxx.com,nagoya

実行結果

$ perl input_csv.pl output_xxxxxxxxxx.csv
(1)
	name : Taro Yamada
	email : taro.yamada@xxx.com
	address : tokyo
(2)
	name : Jiro Tanaka
	email : jiro.tanaka@xxx.com
	address : osaka
(3)
	name : Saburo Abe
	email : saburo.abe@xxx.com
	address : nagoya
$

解答例

CSV書き出しスクリプト
use strict;
use warnings;
use utf8;
use Text::CSV_XS;

binmode STDIN, ':utf8';
binmode STDOUT, ':utf8';
binmode STDERR, ':utf8';

# CSV Parser
my $csv = Text::CSV_XS->new({binary=>1});

# 書き出しファイル名
my $csvout_name = $ARGV[0] || 'output_' . time . '.csv';

# File Check
if (-e $csvout_name) {
  print STDERR "$csvout_name already exists.\n";
  exit 1;
}

# Input Data Table
my @labels = ('name', 'email', 'address');
my @table = ([]);

# Input Data
my $finish = 0;
while ($finish == 0) {
  print $labels[scalar(@{$table[-1]})] . ' : ';
  if (my $param = <STDIN>) {
    $param =~ s/[\r\n]//go;
    if ($param ne '') {
      push @{$table[-1]}, $param;
      if (@{$table[-1]} >= @labels) {
        push @table, [];
      }
    }
  } else {
    $finish = 1;
    if (@{$table[-1]} > 0) {
      for (my $cnt = @{$table[-1]}; $cnt < @labels; $cnt++) {
        push @{$table[-1]}, '';
      }
    } else {
      pop @table;
    }
  }
}
print "\n";
unshift @table, [@labels];

# File Double-check
if (-e $csvout_name) {
  print STDERR "$csvout_name already exists.\n";
  exit 1;
}

# CSV Output
open my $csvout, '>:utf8', $csvout_name;

foreach my $row (@table) {
  $csv->combine(@$row);
  print $csvout $csv->string . "\n";
}

close $csvout;

exit 0;
CSV読み込みスクリプト
use strict;
use warnings;
use utf8;
use Text::CSV_XS;

binmode STDIN, ':utf8';
binmode STDOUT, ':utf8';
binmode STDERR, ':utf8';

# CSV Parser
my $csv = Text::CSV_XS->new({binary=>1});

# Input Check
if (@ARGV != 1) {
  print STDERR "Please input CSV file name.\n";
  exit 1;
}

# 読み込みファイル名
my $csvin_name = $ARGV[0];

# File Check
unless (-f $csvin_name) {
  print STDERR "$csvin_name is not found.\n";
  exit 1;
}

# Input CSV
open my $csvin, '<:utf8', $csvin_name;

my @table = ();
while (my $line = $csv->getline($csvin)) {
	push @table, $line;
}

close $csvin;

# Output CSV Data
my @labels = (@table > 1) ? @{(shift @table)} : ();

for (my $rowcnt = 0; $rowcnt < @table; $rowcnt++) {
  print '(' . ($rowcnt + 1) . ')' . "\n";
  for (my $colcnt = 0; $colcnt < @{$table[$rowcnt]}; $colcnt++) {
    print "\t";
    if (@labels > 0) {
      print $labels[$colcnt] . ' : ';
    }
    print $table[$rowcnt][$colcnt] . "\n";
  }
}

exit 0;