PerlIOのレイヤについてまとめてみた

PerlIOがいつまでたってもきちんと理解できず、毎回試行錯誤しながら適当に指定してしまうのできちんと調べてみた。


調べてみて一番の収穫だったのは、レイヤは主に三種類に分けられる、ということ。
ドキュメントでは順不同で出てくるのでごっちゃになって混乱していたが、こうして分類してみると自分の望むものが何なのかよくわかるようになった。

リソースアクセス系

これはリソースに対して実際にIOを行うレイヤ。

:unix

Unix/POSIXのread(), write()等を使うレイヤ。
バッファリングしない。どのプラットフォームでも常にO_BINARYで扱う。

:win32

file descriptorの代わりにWindowsネイティブのハンドルを使うレイヤ。
5.10.0現在のリリースではバグっぽいことがわかっているらしい。ので使わないほうがいい。

:stdio

システムのstdioライブラリを使うレイヤ。バッファリングされる。
perlrunにはCRLF変換は行われないって書いてあるけど、StrawberryPerlで試したら変換されたんだが。

:mmap

mmap()を使ってファイルをプロセスのアドレス空間にマップしてアクセスするレイヤ。

:scalar

スカラ変数を入出力にマップするレイヤ。

リソース加工系

これはデータを加工するレイヤ。

:perlio

stdioのようなバッファリングをPerlIOとして実装したレイヤ。

:crlf

MS-DOS/WindowsのようなCRLFと\nの変換を行うレイヤ。

:encoding

エンコーディング変換を行うレイヤ。
バイト列とPerl's internal stringの変換を透過的に行う。

: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にも影響が出るので、読み込んだモジュールのリテラルが壊れたりする。

その他の拡張レイヤ

これらの他にCPANには色々なPerlIO拡張がある。
PerlIO::gzipとかはありがちだけど、PerlIO::Utilに含まれている:teeとかは面白そう。