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 を指定することによってエンコードできなかったときに例外を発生させるようにしてみた。それでこれを実行すると次のようになった。
f:id:ks0608:20120306142328p:plain
あれ? 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";
}

これを実行すると
f:id:ks0608:20120306143424p:plain
で期待した結果が得られた。

結論として、Encode モジュールの encode, decode 関数に第3変数を指定して使うとき、破壊的な関数になるかもしれないので気をつけろ、と分かった。

今日の疑問

  • よく見てみると iso-2022-jp のところが変だ。文字 ä を iso-2022-jp にすると 1b 24 28 44 3f 3f 1b 28 42 になってるが、これは文字「扤」のコードだ。なんか間違ってるのか?
  • cp932 も変だ。61 は普通の小文字 a のコードだ。