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つ対応していくのがいいんでしょうか。