- 2004-04-09 (金)
- Perl
search.cpan.org XML::Simple
↑基本的な使い方はCPANにすべて記載されてます(当たり前)。
ですので作者本人も触れていない、「日本語を含んだXML」をXML::Simpleで取り扱う際のポイントをココで紹介してみようかと思います
(仕事で使う際にbashi本人が調べて気づいた事のみになりますが)。
※文字コード「EUC-JP」または「SHIFT_JIS」の日本語を扱う場合のみ下記指摘は該当します。「UTF8」の日本語文字のみ使用している場合は問題無いように見えます。
XML → Perl
で、前回の続き。
XMLヘッダタグをつけてあげると日本語を含むXMLデータも正常に XMLin メソッドで取り込める、という所まで前回書きました。では今回は取り込んだ後のPerl構造体について。実際に取り込んだ後の構造体を出力してみると不具合は一目瞭然です。
※以下、文字コード「EUC」の端末上でPERLを実行した結果↓
use XML::Simple; $xml = qq(<?xml version="1.0" encoding="euc-jp" ?> <opt> <bronze> <dragon>紫龍</dragon> <pegasus>星矢</pegasus> <phoenix>一輝</phoenix> </bronze> </opt> ); $x = new XML::Simple; my $p = $x->XMLin($xml); use Data::Dumper; print Dumper($p); [結果] $VAR1 = { 'bronze' => { 'phoenix' => '筝莠', 'pegasus' => '肄▼,唱 'dragon' => '膣・ } };
文字化けていますね...。
というか日本語部分はすべて文字コード「UTF8」に変換されているんです。これは XML::Parser の仕様で、
取り込む文字列はすべて「UTF8」に変換される
からです。取り込むXMLデータの文字コードが「EUC」だろうが「Shift_JIS」だろうが、XML::Parserはすべて「UTF8」にコード変換かけてくれます。自分は
EUCのXMLを取り込む → EUCのPerl構造体を作成
するのが目的だった為、ここで軽く舌打ち。XML::Parserのdocumentを詳しく調べれば素敵なオプション指定が見つかるのかもしれませんが、ザッと見た感じ、取り込むXMLデータの encoding に関するオプション指定はあるものの(ProtocolEncoding ハンドラとか)、取り込んだ後のPerl構造体の encoding に関しての記述は無い雰囲気。というわけで、
取り込む文字列はすべて「UTF8」に変換される
という点に気をつけてください。
※今回は「EUCのPerl構造体」をXMLから作成するのが目的なので、以下のような「構造体の文字コードを一括変換」する自前関数をさっくりこしらえて対応しました。
sub ConvertEncoding($$$){ use Jcode; my($p,$from,$to) = @_; die qq(ConvertEncoding : need to specify character encoding you'd want to convert to) if(!$to); my $r = ref($p); ### if [BLESSED] HASH REFERENCE ### if($r eq 'HASH' || ( !$r && $p =~ /=HASH\(.+\)/ )){ my $v; foreach $v (keys %{$p}){ $p->{$v} = &ConvertEncoding($p->{$v},$from,$to); } } ### if [BLESSED] ARRAY REFERENCE ### elsif($r eq 'ARRAY' || ( !$r && $p =~ /=ARRAY\(.+\)/ )){ my $i=0; for ($i;$i <= $#{$p};$i++){ $p->[$i] = &ConvertEncoding($p->[$i],$from,$to); } } ### if [BLESSED] CODE REFERENCE ### elsif($r eq 'CODE' || ( !$r && $p =~ /=CODE\(.+\)/ )){ } ### if SCALAR ### else{ $p = Jcode->new($p,$from)->$to; } return $p; }
以下、実装例:
use XML::Simple; $xml = qq(<?xml version="1.0" encoding="euc-jp" ?> <opt> <bronze> <dragon>紫龍</dragon> <pegasus>星矢</pegasus> <phoenix>一輝</phoenix> </bronze> </opt> ); $x = new XML::Simple; my $p = $x->XMLin($xml); $p = &ConvertEncoding($p,"utf8","euc"); use Data::Dumper; print Dumper($p); [結果] $VAR1 = { 'bronze' => { 'phoenix' => '一輝', 'pegasus' => '星矢', 'dragon' => '紫龍' } };
関連情報
XML::Simple は遅い説における意外な落とし穴
XML::Simple は遅い説における意外な落とし穴 - おまけ編
- Newer: 馬場二郎にミニラーメン登場
- Older: Motley Crue の Vince Neil