はちゅにっき

こっちのブログはまったり更新

Perl で Excel を Parse する

Perl で Excel を Parse するときのお約束といえば

Spreadsheet-ParseExcel
http://search.cpan.org/~jmcnamara/Spreadsheet-ParseExcel-0.54/lib/Spreadsheet/ParseExcel.pm

ですよね!
というわけで、今回の話題は Perl で Excel を Parse する方法について。
ただし、Excel にはイタリア語とか、フランス語とか、日本語とか。。。
とにかくイロイロな国の文字が入力されているという、稀によくある状況を想定しています。

が、ハングル文字とかキリル文字とか、アジア圏の文字全部に対応できているかは謎です。
アジアってすごいよね。。。言語的意味で。

ちなみに日本語のみの Excel では、以下のモジュールを使うと便利だそうです。

Spreadsheet-ParseExcel-FmtJapan
http://search.cpan.org/~jmcnamara/Spreadsheet-ParseExcel-0.54/lib/Spreadsheet/ParseExcel/FmtJapan.pm

あんまり利用したことないけれど、きっとこんな感じ。

use Spreadsheet::ParseExcel;
use Spreadsheet::ParseExcel::FmtJapan;

our $PARSE_FILE = 'hogehoge.xls';

my $excel =  Spreadsheet::ParseExcel->new;

# Excel を Parse
my $book =  $excel->Parse($PARSE_FILE, SpreadSheet::ParseExcel::FmtJapan->new); 

# 1番目のシートを取得
my $sheet = $book->worksheet(0);

# Cell の値取得
my $cell = $sheet->get_cell(0, 1);

print $cell->value, "\n";

Parse の第2引数として Formatter*1 を指定すれば日本独自の年号とか、(株) とかが入っていても、特に何も考えずに Parse することができるみたいですね。
そのあたりは以下のサイトなどが役に立ちそうです。

Excelファイル読み込めるSpreadsheet-ParseExcel(Perl)
http://blog.setunai.net/20080511/excel%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E8%AA%AD%E3%81%BF%E8%BE%BC%E3%82%81%E3%82%8Bspreadsheetparseexcelperl/


でも、こんな文字 → è のように、イタリア語やフランス語、ドイツ語だとうまく Parse できないみたいで *2 文字化け?*3 してしまいました。
というわけで文字化けに対処すべく、以下のモジュールを Formatter として利用しました。

Spreadsheet-ParseExcel-FmtUnicode
http://search.cpan.org/~jmcnamara/Spreadsheet-ParseExcel-0.54/lib/Spreadsheet/ParseExcel/FmtUnicode.pm

が、FmtJapan と違い、ただ Formatter として指定するだけではどうやらダメなようで。。。
しっかり読み込んだ Cell の値を Encode してあげる必要がありました。
また、XML にするので正しい文字参照に変換する必要もあります。
Encode といえば、当然 Encode.pm なんですが。。。

perl - Encode 中級
http://blog.livedoor.jp/dankogai/archives/51047005.html

なんかすごいジャストな記事っぽい!
というわけで、

my $result
    = defined $cell->{Code}
    ? decode($cell->{Code}, $cell->value, sub{ sprintf "&#%d;", shift })
    : decode('ascii', $cell->value, sub{ sprintf "&#%d;", shift });

こんな風にすると、うまく Parse することができました!
$cell->{Code} に Encode 名が入っているんだけれど、define されていない場合は、ASCII として単に処理してあげればいいみたいですね。
わーい。


あ、ただ1つ残ってしまった疑問は、decode を OO で呼び出した場合、つまり

my $enc = find_encoding('ascii');
$enc->encode($strings);

の場合、第3引数?第2引数?が利用することができませんでした。
OO で呼び出した方がコスト的にはメリットがあるそうですが。。。
うーん。この辺はもうすこし調べてみよう。。。

*1:ここでは FmtJapan

*2:FmtJapan なので当然といえば当然だとは思うけれど

*3:最終的に XML に変換していたため、Firefox では &#FFFD; として表示されていました。