
すびばせん。それ、ドキュメントバグです。
PerlIO の encoding layer の fallback ではまった - daily dayflowerEncode - character encodings - search.cpan.org をみるとわかるように,FB_XMLCREF は XMLCREF | LEAVE_SRC なんだけど,いろいろ試行錯誤してるとどうやら LEAVE_SRC が悪さをするらしい。
$PerlIO::encoding::fallback
を指定するときは、FB_*
を使ってはいけません。
今からその理由を説明します。
Encode::LEAVE_SRCって何さ?
まずは、Encode::FB_(PERLQQ|HTMLCREF|XMLCREF)
とEncode::(PERLQQ|HTMLCREF|XMLCREF)
の違い、すなわちFB_
がついたフラグと、そうでないフラグの違いを見てみましょう。
#!/usr/bin/perl use strict; use warnings; use Encode; use utf8; # for literals; binmode STDOUT, ':utf8'; { my $utf8 = 'dan小飼'; my $ascii = encode 'ascii', $utf8, Encode::FB_XMLCREF; print '$utf8 = ', $utf8, "\n", '$ascii = ', $ascii, "\n"; } { my $utf8 = 'dan小飼'; my $ascii = encode 'ascii', $utf8, Encode::XMLCREF; print '$utf8 = ', $utf8, "\n", '$ascii = ', $ascii, "\n"; }
なんと、FB_
なしの方では、元のデータが消えてしまいました。
実は、CHECKフラグがある時のEncodeの振る舞いは、元のデータを消すのがデフォルトなのです。なぜそうなっているかは後述します。それを防ぐのがEncode::LEAVE_SRC
で、以下のようにFB_*
はこのEncode::LEAVE_SRC
がコミになっています。
is(Encode::FB_PERLQQ, Encode::PERLQQ | Encode::LEAVE_SRC); is(Encode::FB_HTMLCREF, Encode::HTMLCREF | Encode::LEAVE_SRC); is(Encode::FB_XMLCREF, Encode::XMLCREF | Encode::LEAVE_SRC);
通常の使用の場合、元データを「消して」しまうのはたいていの場合望ましくないので、通常はFB_*
をつけるわけですが、それでは困る場合があるのです。
それが、PerlIOです。
PerlIOとバッファー
tie()
でもしていない限り、中味が100%メモリーにあることが保証されているscalarと異なり、Filehandleの中味というのは、ファイル(ストリーム)の一部のみがバッファーの中にあります。そのおかげで、何GBもあるログファイルを、数MBのメモリーで処理できるわけですが、マルチバイト処理の場合、これは困った自体を引き起こす原因となります。
文字がバッファーのおかげで「ちぎれて」しまう可能性があるのです。
たとえば、バッファーが1024byteの時、小飼弾
がずらーっと並んだファイルを読むとしましょう。この時、114個目の小飼弾
はどうなるか? bytes::length('小飼弾')
* 114 = 1026 なので、2byte余ることになります。つまり、こういうことです。
小 |飼 |弾 | \xe5\xb0\x8f\xe9\xa3\xbc\xe5\xbc\xbe == buffer==================]
何も考えずに処理していたら、あわれ弾は"\xe5"と"\xbc\xbe"にギロチンされてしまうのです。
そうならないように、PerlIOから呼び出されたEncodeは、decode()
ないしencode()
できるところまで処理した後、処理しきれなかった部分を、引数に書き込む形でPerlIOに戻します。上の例で元データが「消えていた」のは、実は「全て処理済み」だったのですね。PerlIOは、バッファーを空にするのではなく、処理しきれなかった分に追加する形でバッファーをまた埋め、埋まったら、あるいはeofが来たらまたEncodeを呼ぶというわけです。
その時にEncodeにどう振る舞うかを教えてあげるのが、$PerlIO::encoding::fallback
だったのです。
実はこのあたりの事情は、以下に書いてはあります。
- Encode::PerlIO
- Encode::Encoding -
perlio_ok()
とneeds_lines()
のあたり
こういうのもなんですが、きめの細かいコントロールが欲しい場合はPerlIOを経由するのはあまり望ましいとは言えないでしょう。PerlIOでサポートできないencodingも存在しますし(例えばISO-2022-KR)、fallbackの指定もごらんの通りパッケージ変数経由でやる必要がある。どうしてもという場合には、
$PerlIO::encoding::fallback &= ~Encode::LEAVE_SRC; # LEAVE_SRCを必ず落とす!
ようにしてください。
Dan the Encode Maintainer
このブログにコメントするにはログインが必要です。
さんログアウト
この記事には許可ユーザしかコメントができません。