« 今月「日曜日」が何回あるのか教えてください | メイン | mod_gzipのエラー »

CGI.pmとUTF8 flag

CGI.pmとuse utf8またはuse encodingを一緒に使うと、

#!/usr/bin/perl
use strict;
use warnings;
use utf8;
binmode STDOUT, ":utf8";
my $q = CGI->new;
print $q->header(-charset=>'utf-8');
print "「" , $q->param('test'),"」が入力\n";

これはうまく動かない。testの部分が文字化けする。use utf8、binmodeの代わりに

use encoding "utf8";

としても同じ。

なぜかと言えば、$q->param('test')の戻り値が、utf8 flagがoff、あるいは変な状態でonになっているのが原因だと思われ。use utf8ではflagはたたないし、use encodingではonだけどおかしい。これは、CGI.pmの内部コードで

  $todecode =~ s/%(?:([0-9a-fA-F]{2})|u([0-9a-fA-F]{4}))/
	defined($1)? chr hex($1) : utf8_chr(hex($2))/ge;

chr(hex())を使っている部分

%E6%97%A5%E6%9C%AC%E8%AA%9E(日本語)

を処理するのに、

chr(hex(E6)) . chr(hex(97)) . chr(hex(A5)) ...

となっているために、「use encoding」宣言されていれば、1つ1つはflagがついていきながら処理される(?)のだけど、本当のところは、

chr(hex(E697A5)) ...

こうなっていないと正しいUTF-8な文字にならないだよな。
これは仕方ない。なかなか修正しようがなさそう。

なのでいい方法としては、

use utf8;
binmode STDOUT, ":utf8";
use Encode;
for my $p ($q->param) {
	my @v = map {Encode::decode('utf8',$_)} $q->param($p);
	$q->param($p,@v);
}

として変換する。(これは「use encoding "utf8"」としていても正しく動きました)
あるいは、1つ1つ対応していくのがいいんでしょうか。

トラックバック

この一覧は、次のエントリーを参照しています: CGI.pmとUTF8 flag:

» perl(CGI.pm)とUTF-8 from スクリプト研究所
Perlでも最近UTF-8を扱うことが多くなってきましたが、なかなかUTF-8で... [詳しくはこちら]

» HTML::TemplateをUTF8で使ってみたけど… from PerlとmySQL忍々
UTF8でperlを書くと、いろいろな問題に直面します。 で、今回はCGI.pmとHTML::Templateで早速トラブル(涙)。 perl... [詳しくはこちら]