すびばせん。それ、ドキュメントバグです。
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
このブログにコメントするにはログインが必要です。
さんログアウト
この記事には許可ユーザしかコメントができません。