November 10, 2003

Perl 5.8 Unicode で再びはまり中

[ Perl ]

またしても Perl 5.8 での Unicode ではまっています。ドキュメントにはコマンドラインスイッチ -C や環境変数 PERL_UNICODE で、PerlIO レイヤにおける UTF-8 の扱いを制御できるそうなのですが、どうにもうまくいきません。

以下、はまり記録です。

少し前に 'Perl 5.8 以降においての Unicode 文字列の扱い方' というエントリを書きました。UTF-8 で記述された外部ファイルを open する場合などには PerlIO レイヤを使って明示的に UTF-8 であることを指定すると良い、という話などです。

open $fh, "<:utf8", $file;

といった具合で。自力で外部ファイルを open する場合にはこれでいいのですが、ファイルの open() をモジュールの内部で行っているものを使いたい場合には、上記の方法はモジュールに手を加えるなどしなければ適用できません。

例えば HTML::TemplateTemplate-Toolkit などを内部で使っているフレームワークなど。Sledge で遊んでいるのですが、Sledge は内部で Template-Toolkit を使っていて、Sledge::Template::TT で Template-Toolkit にテンプレートファイルを渡しています。ですので、Sledge::Template::TT に手を入れるとか、Sledge::Template::TT を継承したクラスで該当メソッドをオーバライドするとかしないと、スクリプト内での明示的な utf8 PerlIO レイヤ指定はできないわけですが、そんな場当たり的な修正はナンセンスです。

そこで、utf8 PerlIO レイヤを何か別の手段で指定できないかということになるわけですが、perldoc perlunicode すると、

You can enable automatic UTF-8-ification of your stan-dard file handles, default "open()" layer, and @ARGV by using either the "-C" command line switch or the "PERL_UNICODE" environment variable, see perlrun for the documentation of the "-C" switch.

と記述されており、コマンドラインスイッチか、環境変数でそれが可能とのこと。更に perldoc perlrun すると、-C スイッチの使い方が詳しく載っています。

$ perl -COEio example.pl

とかして使うみたいです。

i     8    UTF-8 is the default PerlIO layer for input streams

とあるので、入力に対する utf8 レイヤの指定は -Ci と指定すれば良さそう。そこでこのオプションを指定していろいろやってみたのですが、どうも期待通りに動いてくれてない様子。

# UTF8 の実験。コマンドラインオプション -C や PERL_UNICODE の動きを探る
# see also: perldoc perlrun
use strict;
use warnings;
# use utf8;
use HTML::Template;
 
my $str = "UTF8の実験です。テスト。";
 
my $fh;
local $/;
 
open $fh, "<:utf8", './test.tmpl' or die $!;
# open $fh, "<", './test.tmpl' or die $!;
 
my $tmpl = HTML::Template->new( filehandle => $fh,
                                die_on_bad_params => 1 );
$tmpl->param( str => $str );
print $tmpl->output;

こんなスクリプトを書いて、PerlIO レイヤに utf8 を指定した場合とそうでない場合、加えて -C スイッチを指定した場合とそうでない場合など、strace で追ってみました。

まず、PerlIO レイヤに utf8 を指定した場合のトレース結果ですが、test.tmpl を open した後に、

stat64("/usr/local/lib/perl5/5.8.1/i586-linux/utf8.pmc", 0xbfffe860) = -1 ENOENT (No such file or di
rectory)
open("/usr/local/lib/perl5/5.8.1/i586-linux/utf8.pm", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such fil
e or directory)
stat64("/usr/local/lib/perl5/5.8.1/utf8.pmc", 0xbfffe860) = -1 ENOENT (No such file or directory)
open("/usr/local/lib/perl5/5.8.1/utf8.pm", O_RDONLY|O_LARGEFILE) = 4
fstat64(4, {st_mode=S_IFREG|0444, st_size=6905, ...}) = 0
old_mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x40023000
read(4, "package utf8;\n\n$utf8::hint_bits "..., 4096) = 4096
brk(0x820f000)                          = 0x820f000
close(4)                                = 0
munmap(0x40023000, 4096)                = 0
open("/usr/local/lib/perl5/5.8.1/i586-linux/utf8_heavy.pl", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No su
ch file or directory)
open("/usr/local/lib/perl5/5.8.1/utf8_heavy.pl", O_RDONLY|O_LARGEFILE) = 4
fstat64(4, {st_mode=S_IFREG|0444, st_size=9464, ...}) = 0
old_mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x40023000
read(4, "package utf8;\nuse strict;\nuse wa"..., 4096) = 4096

といった具合で utf8 関連のモジュールを open していることが分かります。この後に、/usr/local/lib/perl5/5.8.1/unicore 以下のモジュールを open して色々処理を行っているようです。(これ以上は、僕の知識では、トレースを見てもよくわかりませんでした。情けない。)

一方、utf8 を指定しない場合のトレース結果ですが、この場合、上記の utf8 モジュール関連の open や unicore ディレクトリ配下のモジュールの読み出しが一切見当たりません。ということで、utf8 PerlIO レイヤが有効になっているかそうではないかは、この辺りで判別できそうです。

そこで、スクリプト内では特に utf8 PerlIO レイヤは指定せずに、-C スイッチや PERL_UNICODE 環境変数を指定して同じようにトレースを見てみたのですが、-CSD とか -Cio とか PERL_UNICODE="io" とか色々やってみても、どうも utf8 PerlIO レイヤが有効になってくれていない様子でした。うーん。

もしかして Perl のビルド時に何かしらオプションを指定しないと -C スイッチや PERL_UNICODE 環境変数は有効にならないとかあるんでしょうか。それとも何か単純なところで躓いているのかなあ。

困ったぞ。

Posted by naoya at November 10, 2003 02:17 AM | トラックバック (0)  b_entry.gif
トラックバック [0件]
TrackBack URL: http://mt.bloghackers.net/mt/suck-tbspams.cgi/624
コメント [2件]

http://www.xav.com/perl/lib/utf8.html
とか
http://www.xav.com/perl/lib/Pod/perlunicode.html
を見ると
WARNING: The implementation of Unicode support in Perl is incomplete.
と書かれています。

CAVEATS(警告) のところを読むと
As of yet, there is no method for automatically coercing input and output to some encoding other than UTF-8. This is planned in the near future, however.

現時点では、-C を指定しても「utf8 PerlIO レイヤ」を適用すべきかどうか判断する処理が未実装なので、うまくいかないのではないか、と思われます。

(私の勘違いかもしれないですが)

[1] Posted by: tsupo at November 10, 2003 08:00 PM [返信]

確認してみたのですが、これって Perl 5.6.1 の perlunicode ですよね。5.8 では同じ記述は見られませんし、Unicode 周りが正しく実装されたのが 5.8 の大きな特徴だったと思います。

もうちょっと詳しく調べてみますね。情報ありがとうございます。

[2] Posted by: naoya at November 11, 2003 10:29 PM [返信]