XML::Simpleで取得したデータが引き起こす文字化けの対処方法
XML::Simpleを使ったPerlスクリプトの文字化けではまってしまったので、その備忘録です。
1.発生事象
Perlスクリプトで次のXMLファイルを読み込みます。ファイルの文字コードはUTF-8です。
<?xml version="1.0" encoding="utf-8"?>
<list>
<name>ほげ</name>
</list>
Perlスクリプト(CGI)のサンプルは次の通りです。ファイルの文字コードはUTF-8です。
#!/usr/bin/perl
use strict;
use CGI;
use XML::Simple;
my $q = new CGI;
print $q->header(-charset=>'utf-8');
print $q->start_html;
my $xml = XMLin('foo.xml');
my $name = $xml->{name};
print <<EOF;
<form method="post" action="hoge.cgi">
名前:<input type="text" id="hoge" name="hoge" value="$name" />
<input type="submit" name="submit" value="送信" />
</form>
EOF
print $q->end_html;
このCGIをブラウザで表示すると、次のように、スクリプトに記述した全角文字の文字化けが発生します。
XMLファイルから取得する文字が、次のように全角文字でなければ文字化けは発生しません。
<?xml version="1.0" encoding="utf-8"?>
<list>
<name>hoge</name>
</list>
2.原因
XMLinで日本語を含む文字列を取得する場合、XML宣言のencoding属性に指定した文字コードの内部文字列に変換されます。よって外部に出力する場合、バイト文字列に変換(エンコード)する必要があるようです。
Data::Dumperで$nameを出力すると、「ほげ」は次のようになっていました。
$VAR1 = "\x{307b}\x{3052}";
試しに、次のようなサンプルであれば、
my $name = 'ほげ';
Data::Dumperで$nameを出力すると、
$VAR1 = 'ほげ';
となりました。
推測ですが、文字化けが発生したのは、内部文字列とバイト文字列が混在していたため、Perl自体がバイト文字列である「名前」「送信」をさらにエンコードを行い、文字化けが発生したものと思われます(解釈が間違っていたらご指摘ください)。
3.対処
スクリプトの$nameをUTF-8にエンコードすることで解決しました。
#!/usr/bin/perl
use strict;
use CGI;
use XML::Simple;
use Encode;
my $q = new CGI;
print $q->header(-charset=>'utf-8');
print $q->start_html;
my $xml = XMLin('foo.xml');
my $name = encode('UTF-8', $xml->{name});
print <<EOF;
<form method="post" action="hoge.cgi">
名前:<input type="text" id="hoge" name="hoge" value="$name" />
<input type="submit" name="submit" value="送信" />
</form>
EOF
print $q->end_html;
4.その他
出力時だけでなく、スクリプトに記述した全角文字と比較をする際も、予めエンコードしておく必要があります。
my $name = encode('UTF-8', $xml->{name});
if ($name eq 'ほげ') { ... }
以上です。色々調べてこれが最適解と思ったのですが、より適切な解決方法がありましたらコメントください。
2010.6.18 追記
下記の記事で、utf8プラグマの情報頂きました。ありがとうございました。
5.参考サイト
参考サイトは下記です。ありがとうございました。
- Perlでansibleライブラリを作成する方法
- perlのCPANモジュールからRPMを作成する方法
- Perlで「Subroutine permission redefined at~」を抑止する方法
- XML::Simpleのインストールでエラーになる場合の対処
- YAML::Tinyで「YAML::Tiny found bad indenting in line~」というエラーになる場合の対処
- Perlの正規表現を使って文字列をまとめて取得する方法
- Perlのハッシュでキーの有無を調べる方法
- perlで配列の途中の要素を削除する方法
- YAML::Tinyでコロンを利用する方法
- Perlで改行コードがCRのファイルを読み込む方法
- Perlで「Possible precedence issue with control flow operator」という警告の対処
- PerlのLWPで「Can't verify SSL peers without knowing which Certificate Authorities to trust」というエラーになったときの対処
- Perl+Windowsでファイルを再帰的にリネームする方法
- Perlプログラムの中でファイルの一部を書き換える方法
- Perlの「Bareword "%s" not allowed while "strict subs" in use~」というエラーについて
≫ XML::Simpleで文字化けしない方法 from 日曜プログラマのそゞろ事
普段、MovableTypeのプラグインでお世話になっている「小粋空間」さんが、... [続きを読む]
utf8 フラグあたりはなかなか理解しにくいところですが、このあたりが参考になるかもしれません。
http://wiki.bayashi.net/perl/unicode
http://blog.livedoor.jp/dankogai/archives/51031595.html
http://d.hatena.ne.jp/tokuhirom/20080408/1207619640
http://blog.livedoor.jp/dankogai/archives/51290188.html
それでも足りない場合は 'perl utf8 flag' でぐぐると幸せになれるかも。
問題ないように追記するとこうなるのかなぁ。
http://codepad.org/g3Rkgelf