CSV(カンマ区切りのテキスト)なデータを処理するときに、何が厄介かって "(ダブルクォート)で囲まれたフィールドの中に存在する ,(カンマ)だったりします。例えばこんな感じ(↓)のヤツです。
日本,"9,222.52",2012/11/21
アメリカ,"12,836.89",2012/11/22
だったら、そのカンマを取り除いてしまえ! というわけで、プログラムを書いてみました。
#!/usr/bin/perl
use strict;
use warnings;
my $qw = '"'; # 引用符を定義
my $dlm = ","; # 区切り文字を定義
open(RH, "<", "from.csv");
open(WH, ">", "to.csv");
while(<RH>){
print WH RemoveDelimiterInQuotes($_, $qw, $dlm);
}
close(WH);
close(RH);
exit;
sub RemoveDelimiterInQuotes {
my $str = shift; # 対象文字列
my $qw = shift; # 引用符
my $dlm = shift; # 区切り文字
my @substrs = ( $str =~ /$qw[^$qw]*$qw/g);
for my $substr (@substrs){
my $target = $substr;
$substr =~ s/$dlm//g;
$str =~ s/\Q$target\E/$substr/;
}
return $str;
}
from.csv を読み込んでダブルクォートに囲まれた部分のカンマを削除して、to.csv に書き出します。これでバッチリだね!
と、ここまで出来てから、な~んとなくもう一度 Web で検索してみたところ、標準モジュールの Text::ParseWords を使えばもっと簡単に出来たことが判明。あちゃ~。
perlでcsvファイルを読む(ダブルコーテーション内カンマを無視したい) | PerlのQ&A【OKWave】
折角なので Text::ParseWords を使って書き直したのがこちらになります。
#!/usr/bin/perl
use strict;
use warnings;
use Text::ParseWords;
my $dlm = ","; # 区切り文字を定義
open(RH, "<", "from.csv");
open(WH, ">", "to.csv");
while(<RH>){
chomp;
my @parsed = parse_line($dlm, 1, $_);
for(my $i=0; $i<=$#parsed; $i++){
$parsed[$i] =~ s/$dlm//g;
}
print WH join($dlm, @parsed)."\n";
}
close(WH);
close(RH);
exit;
今回は処理内容を同じにするために、あえて to.csv として書き出していますが、parse_line を通した時点でデータはフィールドごとに分かれていますので、そのまま目的の処理をしてしまうのが正しい使い方でしょうね。
参考リンク
・Comma-Separated Values - Wikipedia