PerlIOのレイヤについてまとめてみた
PerlIOがいつまでたってもきちんと理解できず、毎回試行錯誤しながら適当に指定してしまうのできちんと調べてみた。
調べてみて一番の収穫だったのは、レイヤは主に三種類に分けられる、ということ。
ドキュメントでは順不同で出てくるのでごっちゃになって混乱していたが、こうして分類してみると自分の望むものが何なのかよくわかるようになった。
リソースアクセス系
これはリソースに対して実際にIOを行うレイヤ。
:win32
file descriptorの代わりにWindowsネイティブのハンドルを使うレイヤ。
5.10.0現在のリリースではバグっぽいことがわかっているらしい。ので使わないほうがいい。
:stdio
システムのstdioライブラリを使うレイヤ。バッファリングされる。
perlrunにはCRLF変換は行われないって書いてあるけど、StrawberryPerlで試したら変換されたんだが。
:scalar
スカラ変数を入出力にマップするレイヤ。
リソース加工系
これはデータを加工するレイヤ。
:perlio
stdioのようなバッファリングをPerlIOとして実装したレイヤ。
:via
Perlで書かれた透過的IO処理を扱うためのレイヤ。一言で言えばフィルタ。
サンプルとして標準でPerlIO::via::QuotedPrintがある。
PerlIO制御系
これらの擬似レイヤはレイヤスタックには乗らずに、PerlIOに対して様々な制御(フラグの変更など)を行う。
:raw
一般的には、各レイヤでの変換を無効化して、バイナリデータをそのまま扱うように他のレイヤに伝える擬似レイヤ。
内部的には全てのレイヤで独自のbinmodeフックがあればそれを呼び出し、なければそのレイヤを削除する。(この辺自信なし)
binmode(FH)とbinmode(FH, ':raw')は等価。
「:crlfの逆」というのは過去の話であって、今は上記のとおり単なるCRLF変換の無効化ではない。
:utf8
レイヤスタックの(:utf8が指定された時点での)最上位にあるレイヤに対して入力がPerl内部文字列(flagged utf8)であることを伝える(フラグをonにする)擬似レイヤ。
妥当性についてはチェックされないので、UTF-8のデータを読み込みたい場合は:encoding(utf8)を使うことが推奨されている。
:bytes
:utf8の逆で入力をoctetとして扱うことを伝える擬似レイヤ。
flagged utf8を流し込むとwarnを発する。
:pop
最上位にあるレイヤを取り除くための擬似レイヤ。
当然ながらレイヤスタックに積まれない擬似レイヤ(:utf8など)に対しては使えない。
各プラットフォームのデフォルト
UNIXでは:unix:perlioまたは:stdioがデフォルト。
Win32では:unix:crlfがデフォルト。将来的には:win32がデフォルトになる予定らしい。
デフォルトのPerlIOを設定する
環境変数PERLIO
全てのPerlIOに対して影響するため、組み込みのレイヤのみが使える。
:encodingのような外部読み込みレイヤは、それ自身のIOが必要なため指定できない。
openプラグマ
openプラグマを使うことでデフォルトレイヤに:encodingなども指定できるようになるが、トラブルの元なので使わないほうがよいと思われる。
特にuse/requireにも影響が出るので、読み込んだモジュールのリテラルが壊れたりする。
参考
PerlIOの実装メモ#2 PerlIOで出来ること - Islands in the byte stream
open()の実体 - Islands in the byte stream
http://search.cpan.org/~dapm/perl-5.10.1/lib/PerlIO.pm
http://perldoc.jp/docs/perl/5.10.0/perlrun.pod#item_PERLIO_PERLIO
http://perldoc.jp/docs/perl/5.10.0/perlfunc.pod#item_binmode_FILEHANDLE__LAYER_binmode_binary_text_DOS_Windows
http://search.cpan.org/~gfuji/PerlIO-Util-0.70/lib/PerlIO/Util.pm