Perl 5.8 と UNICODE

http://www.pure.ne.jp/~learner/program/Perl_unicode.htmlnaoya 氏の記事でいいつくされているのですが,最近はまっていることがあるので現在の進捗をメモっておきます。

Perl 5.8 以降の文字列スカラーの内部表現

いわゆる UTF-8 ビットがたっているかいないか,また,実際にワイド文字として区切られているかどうか。個別に設定はできるけれど混乱の元になるので,対になっていると思っていればいいです。

my $text = '日本語';
# $text == "\x{e6} \x{97} \x{a5}  \x{e6} \x{9c} \x{ac}  \x{e8} \x{aa} \x{9e}";  # 文字列リテラルは『バイト列』
# utf8::is_utf8($text) == true

UTF-8 表記は,ワイド文字は \x{80} 以降の並びになるので,とくに宣言しなくても『バイト列』としてスクリプトが解釈されます。
一方,スクリプトUTF-8 として宣言した場合,

use utf8;  # スクリプトが UTF-8 で記述されてるよ,と宣言
my $text = '日本語';
# $text == "\x{65e5} \x{672c} \x{8a9e}";  # 文字列リテラルが UCS-2 !!
# utf8::is_utf8($text) == false

のように『UNICODE 列』になります。
前者の例で,UTF-8 ビットの立った文字列を取得したい場合,

use Encode;
$text = Encode::decode('utf8', '日本語');

とすればいいです。「utf8」と「UTF-8」の違いは,perldoc Encode で後ろの方に書いてあります。

Template ToolkitUNICODE

TT での実際のファイル読み込みは,Template::Provider で行われますが,普通にファイルから読み込むと『バイト並び』になってます。一応,UNICODE オプションというのがあって,これを ON にしておくと,ファイルの BOM から文字コードを(ユニコードに限り)判定して,『UNICODE 列』としてパースします。が,なんかこの辺がかえって文字化けの原因になったりしているみたいです。で,宮川さんの Template::Provider::Encoding なるモジュールがあります。これは,デフォルトでは入力を UTF-8 の『バイト列』とみなし,内的には 『UNICODE 列』に変換します。実際には UTF-8 以外の文字コードを指定することも可能です。ともかく,内的に『UNICODE 列』に変換する,というところがキモです。process() した後も『UNICODE 列』なので,自力で変換するか,PerlIO レイヤを使うか,ということになります。

TT::Provider::Encoding でハッピー?

TT が『UNICODE 列』としてパースするということから,以下のようなことができないかな,と考えました。

use utf8;
use Template;
use Template::Context;
use Template::Stash::ForceUTF8;
use Template::Provider::Encoding;

my $tt = Template::Context->new(
  LOAD_TEMPLATES => [
    Template::Provider::Encoding->new
  ],
  STASH => Template::Stash::ForceUTF8->new(
    {
      '日本語変数' => '日本語値',
    },
  ),
);

print Encode::encode('utf8', $tt->process(\*DATA, {}));

__END__
テスト: [% 日本語変数 %]

残念ながら動きません。全部 UTF-8 フラグ付きでがんばっているのに,です。


実は,とりあえず上記のスクリプトを簡単に動かすには…

$Template::Config::STASH = qw(Template::Stash);

してやるといけます。そう,XS モジュールだとなぜかうまくいかないのですね。

結局

じゃあ Template::Stash::XS を改造(もとい拡張)しようと思って perldoc perlapi での utf8 関連の関数を調べてはいたんですが,本質的なことではないので時間切れとなりました。