Perl で UTF-8
オライリーの「CGIプログラミング 第2版」の p.172 に以下のような例が載っている(Embperl の利用例)のだけど、なぜか HTML のテーブル部分の日本語が文字化けして困った。
<html> <head> <title>Table Sample</title> </head> <body> <div align="center"> <h1>$row, $col の実験</h1> [- @sports = ( [ "ウインドサーフィン", "夏", "海・湖" ], [ "スキー", "冬", "山" ], [ "バイク", "通年", "峠" ], [ "キャンプ", "通年", "野原" ] ); -] <table border="1" cellpadding="4" width="400"> <tr> <th>スポーツ</th> <th>シーズン</th> <th>スポット</th> </tr> <tr> <td>[+ decode('UTF-8', $sports[$row][$col]) +]</td> </tr> </table> </div> </body> </html>
ググっていろいろと試行錯誤したところ、どうやら Encode::decode() を使って、Perl の内部形式(UTF-8)に文字列を変換すれば、文字化けせずに表示できることが分かった。以下にコードを載せておく。
[- use utf8; use Encode; -] <html> <head> <title>Table Sample</title> </head> <body> <div align="center"> <h1>$row, $col の実験</h1> [- @sports = ( [ "ウインドサーフィン", "夏", "海・湖" ], [ "スキー", "冬", "山" ], [ "バイク", "通年", "峠" ], [ "キャンプ", "通年", "野原" ] ); -] <table border="1" cellpadding="4" width="400"> <tr> <th>スポーツ</th> <th>シーズン</th> <th>スポット</th> </tr> <tr> <td>[+ decode('UTF-8', $sports[$row][$col]) +]</td> </tr> </table> </div> </body> </html>
なぜこうするとうまくいくのか?
自分が参考にしたの第8項と「perldoc Encode」によると、
8. Embperl and the utf-8 flag Strings stored to %udat keep their utf-8 flag, so you don't need to worry about that. %fdat is different, however (Gerald has it on the post-2.0 TODO list). Here also applies what I said in 7. %fdat can be converted by doing foreach my $k (keys %fdat) { utf8::decode($fdat{$k}); }
perldoc Encode(CAVEAT に書かれていることが重要)
$string = decode(ENCODING, $octets [, CHECK]) Decodes a sequence of octets assumed to be in ENCODING into Perl’s internal form and returns the resulting string. As in encode(), ENCODING can be either a canonical name or an alias. For encoding names and aliases, see "Defining Aliases". For CHECK, see "Handling Malformed Data". For example, to convert ISO-8859-1 data to a string in Perl’s internal format: $string = decode("iso-8859-1", $octets); CAVEAT: When you run "$string = decode("utf8", $octets)", then $string may not be equal to $octets. Though they both contain the same data, the utf8 flag for $string is on unless $octets entirely consists of ASCII data (or EBCDIC on EBCDIC machines). See "The UTF-8 flag" below.
perldoc Encode(同じくCAVEAT に書かれていることが重要)
CAVEAT: The following operations look the same but are not quite so; from_to($data, "iso-8859-1", "utf8"); #1 $data = decode("iso-8859-1", $data); #2 Both #1 and #2 make $data consist of a completely valid UTF-8 string but only #2 turns utf8 flag on. #1 is equivalent to $data = encode("utf8", decode("iso-8859-1", $data));
とのことなので、恐らく、「テーブル内のコードで特別な変数名 $row, $col, $cnt を使うと、動的にテーブルの構築を行う」という Embperl の仕組みを使うと、utf-8 flag が OFF になってしまうから、utf-8 flag を ON にする目的のためだけに、(変換しなくてももともと UTF-8 の $string を) Encode::decode('UTF-8', $string) としなければならないんだと思う。
perldoc Encode の「The UTF-8 flag」っていう章を読んでみると、UTF-8 辺りの話は結構複雑みたい(utf8 と UTF-8 の扱いが違ったり…)。とりあえず問題は解決したので、また困ったらこのエントリを読み返そう。
いろいろ書いたけど、正直、よく理解してないので、もし間違ってたら突っ込んでください。
参考
ソース中の文字列と、DBから取得した文字列を同時に表示すると文字化け - Perl - 教えて!goo
perldoc utf8
perldoc Encode
perldoc perlunicode