Perl - Encode::LEAVE_SRC を使う
を読みながら文字コードについて勉強している。
実際に自分でも何か書いてみようと思い、文字 ä がいろいろな文字コードだと、どんなバイトで表されるかを調べるプログラムを書いてみた。このとき、もしある文字コードで文字 ä に対応するコードがないときは、can't map と表示するようにしたかったので次のようにした。(このソースコード自体は utf8 で保存する。)
#!/usr/bin/env perl use strict; use warnings; use utf8; use Encode; my $string = 'ä'; my @encodings = qw{ ascii iso-8859-1 iso-8859-15 cp437 cp1252 cp932 shiftjis euc-jp iso-2022-jp utf8 UTF-16BE UTF-16LE }; for my $encoding (@encodings) { my $encoded_string; eval { $encoded_string = encode( $encoding, $string, Encode::FB_CROAK ); }; if ($@) { print "$encoding: can't map.\n"; next; } my @hex_dump = map unpack('H2', $_), split(//, $encoded_string); print "$encoding: ", join(q{ }, @hex_dump), "\n"; }
Encode モジュールの encode 関数で該当の文字コードにエンコードさせていて、その第3変数に Encode::FB_CROAK を指定することによってエンコードできなかったときに例外を発生させるようにしてみた。それでこれを実行すると次のようになった。
あれ? iso-8859-15 から何も表示されていない。なぜだ?
調べてみたら
に書いてあった。encode 関数に Encode::FB_CROAK などを指定すると第2変数を上書きすることがあるらしい。それを防ぐには Encode::LEAVE_SRC を bitwise-OR しろと書いてある。すなわち次のようにする。
#!/usr/bin/env perl use strict; use warnings; use utf8; use Encode; my $string = 'ä'; my @encodings = qw{ ascii iso-8859-1 iso-8859-15 cp437 cp1252 cp932 shiftjis euc-jp iso-2022-jp utf8 UTF-16BE UTF-16LE }; for my $encoding (@encodings) { my $encoded_string; eval { $encoded_string = encode( $encoding, $string, Encode::FB_CROAK | Encode::LEAVE_SRC # 縦棒で bit の or をする ); }; if ($@) { print "$encoding: can't map.\n"; next; } my @hex_dump = map unpack('H2', $_), split(//, $encoded_string); print "$encoding: ", join(q{ }, @hex_dump), "\n"; }
これを実行すると
で期待した結果が得られた。
結論として、Encode モジュールの encode, decode 関数に第3変数を指定して使うとき、破壊的な関数になるかもしれないので気をつけろ、と分かった。
今日の疑問
- よく見てみると iso-2022-jp のところが変だ。文字 ä を iso-2022-jp にすると 1b 24 28 44 3f 3f 1b 28 42 になってるが、これは文字「扤」のコードだ。なんか間違ってるのか?
- cp932 も変だ。61 は普通の小文字 a のコードだ。