Hatena::ブログ(Diary)

Islands in the byte stream

2011-02-23

Tips on IEC (implicit encoding conversion)

Perlにおいて日本語のテキスト文字列とバイナリ文字列*1を結合すると激しく文字化けするのは誰もがつまづくトラップですが、これはPerlのデフォルトのIECが Latin-1 に基づいて行われるからです。UTF-8ではなくLatin-1なのは後方互換のために必要な決定なのですが、我々日本人にとってはこのせいで文字化けに苦しまれることになってしまいました。

そこで、IECが発生したときに致命的エラーを発生させるプラグマを書いてみました。

`no encoding::implicit` によって、そのスクリプト全体でIECを禁止します。これはencoding::warningsプラグマとほとんど同じですが、デフォルトで警告ではなく致命的エラーであること、スクリプト全体に効果があることが異なります。これにより文字化けの発生箇所が明かになり、デバッグが容易になるのではないかと思います。

ところで、これはあまり知られていないことですが、IECは標準のencodingプラグマによって変えられるのでした。では、encodingプラグマを使えば文字化けに苦しめられることもなくなるのでしょうか。

試してみます。

Foo.pm:

package Foo;
sub cat { # 適当な文字列結合関数
    my($x, $y) = @_;
    return $x . $y;
}
1;

app.pl:

#!perl -w
use 5.10.0;
use strict;
use encoding 'utf-8-strict';
use Foo;

my $x = 'こんにちは!';
my $y = '世界!';
utf8::encode($y); # バイト列にする
say Foo::cat($x, $y); # 通常は文字化けするが…?
__END__

実行:

$ perl app.pl
こんにちは!世界!

encodingプラグマの効果がレキシカルスコープでないのが幸いし、Foo.pmの中でもUTF-8でIECが行われています。encodingプラグマは副作用が強いので非奨励と思っていましたが、`use encoding 'utf-8-strict'`に限ればむしろプログラマの負担を軽減することになるのではないでしょうか。

ただし、プログラミングモデルとしては、入力で明示的にデコードするのが正しいとは思います。また、IEC はコストが高いため、これに頼るとパフォーマンスに影響が出る可能性があります。したがって、実際にはencoding::implicitを使って事前に検証し、IECには頼らない方がいいかもしれません。