備忘録:Perlのprint(%Dumper)出力の日本語文字化け&Wide character in printの対策

 こんにちは、豆珈琲です。
産業医に「図書館通勤しておいで」とお達しを受け、カフェで作業をしています。

 今回は「Perlで日本語をprint出力をすると、文字化け&なんか警告文出るやん!」と、心のままに叫ぶ現象について、備忘録付けていきます。

 問題が発生するサンプルコード

 簡単にcsvファイルを読み込んで、Dumperを使ってprint出力させるコードです。

use Text::CSV::Slurp;
use Data::Dumper;

my $csv = Text::CSV::Slurp->load(file => 'story.csv');

print Dumper $csv;

  これをそのまま実行すると、

$VAR1 = [

          {

            'title' => "\x{3042}\x{3089}\x{3059}\x{3058}",

            'id' => '1',

            'main_text' => "\x{5168}\x{3066}\x{306f}\x{3053}\x{306e}\x{30a6}\x{30a3}\x{30f3}\x{30c9}\x{30a6}\x{306e}\x{4e2d}\x{306b}\x{3042}\x{308b}\x{3002}"

          },

          {

            'title' => "\x{30c6}\x{30b9}\x{30c8}",

            'id' => '999',

            'main_text' => "\x{30c6}\x{30b9}\x{30c8}\x{3002}/\x{30c6}\x{30b9}\x{30c8}2\x{3002}\x{30c6}\x{30b9}\x{30c8}3\x{3002}/\x{30c6}\x{30b9}\x{30c8}\x{ff01}\x{ff01}"

          }

        ];

 

 となり、\x{3042}\x{3089}\x{3059}\x{3058}など日本語が文字化けしてしまいます。

また、

print $csv->[0]->{title};

と、Dumperを使わずにprintだけで出力させた場合も同様の現象が起きています。

文字化け

Dumper利用時文字化けの原因

 文字化けの原因はutf-8フラグがonになっていることにより、Dumper内 Data::Dumper::qquote() メソッドがエスケープさせていることが原因のようです。

Data::DumperでUTF-8フラグつき文字列をエスケープさせないようにするには - ku

のサイトがわかりやすく解説していたので、こちらを参考にソースコードを書き換えます。

Dumper利用時文字化けの対策

use Text::CSV::Slurp; use utf8; use Data::Dumper;
{ package Data::Dumper; sub qquote { return shift; } } $Data::Dumper::Useperl = 1; my $csv = Text::CSV::Slurp->load(file => 'story.csv'); $is_flagged = utf8::is_utf8{ $csv->[0]->{title}};#utf-8フラグ判定 print ( "utf8 flag: " . ($is_flagged ? 'true' : 'false') . "\n" ); print Dumper $csv->[0]->{title};

 これで内容が正しく判定されて、見ることが出来ます。
個人的にはDumperは開発時に利用するものだと思うので、ローカルモジュール化した方がコード的には美しいですが、それはまた今度にします。

Dumperを利用しない&Wide character in printの対策

 Dumperを利用しない場合は、PerlIOをレイヤに指定すれば、文字化けとWide character in print対策は大丈夫です。

binmode(STDOUT, ":utf8");
 PerlIOとは?

 (突きたくないところを、どうやらつついてしまったので勉強しました)
 PerlIOとは入出力の際にフォーマット変換をするためのフィルタのことです。
 上のコードでは標準出力をutf8にしています。

ざっくりまとめ

 標準出力に関しては、utf8フラグを落とす、フラグを使って判定をかける、標準入出力の文字コードを変換する、のどれかで対策はできそうです。

 今回はDumper内でエスケープする処理が入っていましたが、モジュールを使う際にはエスケープがされてないかの確認が必要ですね……。

参考URL:

Perl: 文字コードとutf8フラグについて - @bayashi Wiki

Data::DumperでUTF-8フラグつき文字列をエスケープさせないようにするには - ku

【Perl 文字化け解決】Wide character in print atといふエラーが原因だったー - 稲沢市よりお届けしてます。

Perl IO レイヤを使って文字コード変換を行う [Perl]