YAML::Syck::Dumpしたら文字化けした件

ちょっとこしょこしょとperlでコードを書いてたんですが、YAML::Syck::Dumpを使ったら出力がなぜか文字化け(まあ正確にはニュアンスがちょっと違う気がするけど)
うーむ。なんだろなーと少し悩んで解決方法を探してるうちに色々わかったのでメモ。


少し前振りを。


事の発端?はCentOSなんですね。最近はもっぱら仕事で使う事が多いのでCentOSを使う事が多いのですが、CentOSのデフォルトの文字コードUTF-8なんですよ。
丁度手前のエントリでも書きましたが、サーバに入ってターミナルでvi使って直でコード書くことが多いんですね。(元々基本一人でコード書く事が多いし、ガリガリかいて即動くか試すってタイプなんで。)ということで書いたソースファイル自体がUTF-8なんですよね。んで、どこで見知ったか

ソースコード自体がUTF-8の時はuse utf8;を書いておく

っていう生半可な知識を手に入れてたので最近頭で宣言するようにしてたんですよ。
普通にコード書いてる分には特に問題なかったんですけど、ついさっきコード書いてたらタイトルの様な症状にみまわれましたよって感じです。

perlでのUTF-8について調べてみた所、ミソはこの辺りでした。

  • perlは5.8.xから内部的にUnicodeで扱われるようになった。
  • ソースコードUTF-8の場合「use uft8;」しておくと文字列にUTF-8フラグがつく
  • 外部から読みこんだファイルにはフラグがたっていない(例えそのファイルがUTF-8であっても)

フラグが有効、無効とは具体的には


フラグが無効であれば、スカラ内のバイト列は、シングルバイトエンコーディングとして、解釈されます。
フラグが有効であれば、スカラ内のバイト列は、 (マルチバイトの、可変調の)
UTF-8エンコードされたキャラクタのコードポイントとして、解釈されます。
ということらしいです。まあ平たく言うと、

  • フラグがたっていないなら単なるバイト列として扱う
  • フラグがたっているならUTF-8の文字列として扱う

といった感じでしょうか。動作を試すために以下のようなコードでテストしてみました。

■フラグが立っている場合


#!/usr/bin/perl

use strict;
use warnings;
use utf8;

my $test = 'テスト';

print utf8::is_utf8($test) ? "フラグたってるって!とりあえず攻めとけよ!\n" : "たってねーよ(笑)\n";

•結果


フラグたってるって!とりあえず攻めとけよ!


見れば分かると思いますが、フラグがたっているかどうかはutf8::is_utf8()で調べることができます。


■フラグが立っていない場合


#!/usr/bin/perl

use strict;
use warnings;

my $test = "テスト";

print utf8::is_utf8($test) ? "フラグたってるって!とりあえず攻めとけよ!\n" : "たってねーよ(笑)\n";

•結果


たってねーよ(笑)


「use utf8;」するか否かでちゃんと結果が違いますね。
ついでにyamlを使ったテストも。

•コード


#!/usr/bin/perl

use strict;
use warnings;
use utf8;

use Encode;
use YAML::Syck;

my ($yaml, $flag, $dump1, $dump2);

$yaml = &YAML::Syck::LoadFile('./flag.yaml');

$flag = $yaml->{'flag'};

# 判定
print utf8::is_utf8($yaml->{'flag'}) ? "フラグたってるって!とりあえず攻めとけよ!\n\n" : "たってねーよ(笑)\n\n";

# Dumpして出力
$dump1 = YAML::Syck::Dump($yaml);
print "$dump1\n";

# フラグをonにしてからDumpして出力
&Encode::_utf8_on($yaml->{'flag'});
$dump2 = YAML::Syck::Dump($yaml);
print $dump2;

yamlファイル


flag: テスト

•結果


たってねーよ(笑)

flag: "\xE3\x83\x86\xE3\x82\xB9\xE3\x83\x88"

flag: テスト

ご覧の通り外部(yaml)から読みこんだ値は「use utf8;」していてもフラグがたっていない状態となりました。これをそのままDumpすると見事に化けてしまいます。
でもコードのなかでフラグを立てることで回避する事ができます。
フラグを立てるにはご覧の通り、


&Encode::_utf8_on()

を使えば良いわけですね。これで見事に解決できました。

しっかし、文字コードはいつまでたっても悩みのタネですねぇ....

※こちらのサイトの説明がわかりやすかったです。

http://www.rwds.net/kuroita/program/Perl_unicode.html

追記

id:xaicron様から貴重なコメントをいただきました。文字化け解消に関するもっとスマートな方法です。

技術的な話はこちらをメインでされてる模様。

http://blog.livedoor.jp/xaicron/