File::BOM via PerlIO::via
いまいじっているウェブアプリの設定は YAML 形式にしています。デザイナさんに設定ファイルをいじってもらったら「コンテンツがそっくり消えてしまいした〜(泣)」と言われてしまいました。ぱっとみ確認したところ形式が壊れたわけじゃなさそうだし…とよくよくみてみたら先頭に BOM がついてました*1。調べてみたところ,Plagger の謎 と同じ現象じゃないですか。
ということで,File::BOM なるモジュールと PerlIO::via インタフェースがあることを知りました。
(毎度毎度周回遅れな状況でため息がでてしまいます)
File::BOM はファイルの BOM からエンコーディングを判別して自動変換してくれるもので,PerlIO::via による ':via' インタフェースを使うとエンコーディングにかかわらず透過的に読み書きできます。といっても
- BOM があるのは UTF-8/16/32(BE/LE) あたりなのでそれら以外は読み書きできない(色々自動判別したいなら,Encode::Guess なり Encode-Detect*2 を使う),
- BOM がないとなにもしない(UTF-8 とみなされる)
んですが。
$buf は utf-8 bytes であって UTF-8 flagged 文字列ではないことに注意。
とのことなので,
use utf8; use File::BOM; open my $fh, '<:via(File::BOM)', $file or die "$file: $!"; my $str = join '', <$fh>; close($fh); print utf8::is_utf8($str), "\n"; print $str, "\n";
のように,同じようなスクリプトを書いてみたところ,
1 Wide character in print at test.pl line 20. ファイルの中身
あらら UTF-8 flagged な文字列みたいですよ。
んーなんでだ,とちょっともぐって調べてみました*3。
- ドキュメントを読むと PerlIO::via の限界で read() を使うとバイトストリームのままらしい
- で read に書き換えてやってみたけどやっぱり UTF-8 flagged な文字列だった
- File::BOM::UTF という関数(see PerlIO::via)が,
- Perl 5.8.7 以降だと 1 を 返す
- 以前だと 0 を返す(バグ回避?)
- ドキュメント古い?
結局 Perl 5.8.7 以降だと UTF-8 flagged で正解のようです。
Perl 5.8.6 以前だとレイヤ2段重ねで
open my $fh, '<:via(File::BOM):utf8', $file
ってすれば同じく UTF-8 flagged な文字列をとれるのではないかと。