perlの文字コード周りはなかなかカオスです。外部エンコードとか、perl内部での文字コードとか、UTF8フラグとか。UTF8フラグ?なにそれ?な人は、こことかここを見てみると良いかも。(・∀・)

基本的には外部から入ってきた時点でdecodeして、出力時にencodeしてやれば全て解決するんですが、「〜(波ダッシュ)」と「−(全角マイナス)」だけは特別です。注意が必要なのはこの2パターン。

① utf8⇔shift_jis
② utf8⇔euc-jp

①については以前、perl utf8→sjisで文字化けという記事で紹介しましたが、encode時に、'sjis'では無くて、'cp932'を指定すればOK。※「〜」とか「−」はsjisには含まれていない文字なのが原因。

今回紹介したいのは②の場合です。(*・ω・)ノ これがまた厄介・・・。

普通に

my $data = "〜−あいうえお"; # 文字コードはutf8
my $encoded = encode('euc-jp', decode_utf8($data));

とかすると、

??あいうえお

見事にこんな感じ。はてな出まくりです(;´Д`) 変換するにはこの方法を使いました。

my $data = "〜−あいうえお"; # 文字コードはutf8
my $encoded = encode('cp932', decode_utf8($data));
my $again = encode('euc-jp', decode('shift_jis', $encoded));

一旦cp932に変換してから、それをshift_jisでdecodeし、euc-jpに変換しています。。なんか凄くバッドノウハウ (#`皿´) でも、この2パターンさえわかっていれば、perlの文字コードではまることもあまり無いんじゃないでしょうか??

まとめると・・(逆も同様です)

① utf8からshift_jisに変換するとき => cp932でencodeする
② utf8からeuc-jpに変換するとき
    => cp932でencodeした後、shift_jisでdecodeし、euc-jpでencodeする

ということで、波ダッシュの文字コード変換のまとめでした〜

【090225追記】

ブクマコメントいただきました!コメントに書かれていたURLを見てみると、

$data =~ tr/\x{ff5e}\x{ff0d}/\x{301c}\x{2212}/;

・・・な、なるほど。要はあれですね。あ、あれですよ。(ここで調べましたw)そもそもこの波ダッシュさん。実は2種類あります。

「〜」← WAVE DASH(U+301C, E3809C)
「〜」← FULLWIDTH TILDE(U+FF5E, EFBD9E)

2つあるのは・・・間違えたっぽいです。。。・゚・(ノД`) どうもこんな感じ。

「〜」←最初に作った。
「〜」←やば、間違えちゃったよ。じゃあ全角チルダでおk (=゚ω゚)ノ

........orz........ このせいで大変なことに。

utf8(unicode)には、いわゆる波ダッシュを表す文字が2つあります。で、基本的には波ダッシュ = FULLWIDTH TILDEと考えます。しかし、shift_jisの「〜」はWAVE DASHとマッピングされています(※これが従来の正しい仕様)。そのため、utf8⇔shift_jisすると文字化けが起こります。一方で、utf8⇔cp932ではcp932の「〜」はFULLWIDTH TILDEにマッピングされています。ので文字化けしません(※完全に後付けのルール)。あと、utf8⇔euc-jpでも、euc-jpの「〜」はWAVE DASHにマッピングされているようです。( ・Д・) まとめるとこうなります。

shift_jis(〜) <=> utf8(FULLWIDTH TILDE) <=> utf8(〜)
cp932(〜) <=> utf8(WAVE DASH)
euc-jp(〜) <=> utf8(WAVE DASH)

ここで以前書いたこれに戻りますが、

utf8からeuc-jpに変換するとき
   => cp932でencodeした後、shift_jisでdecodeし、euc-jpでencodeする

最初、utf8でdecodeした時点で、内部的には\x{55fe}(つまりFULLWIDTH TILDE)です。その後、cp932でencodeし(「〜」が得られます)、これをshift_jisでdecodeすると内部的には\x{301c}(つまりWAVE DASH)が得られます。これとeuc-jpなら問題なく変換できるので文字化けせずに変換できた、と。

つまり、これと同じような処理が行われているわけですね。結局、このように置換すればおkです(^^;)

# $dataはdecodeされたデータ
$data =~ tr/\x{ff5e}/\x{301c}/;

ちなみにutf8からshift_jisに変換するときにも、

my $data = "〜−あいうえお";
$data =~ tr/\x{ff5e}\x{ff0d}/\x{301c}\x{2212}/;
my $encoded = encode('shift_jis', $data);

このようにすることで、(cp932を使わずに)変換することが出来ます。

以上。とりあえず今の理解だとこんな感じです。にしても、「〜」と「〜」を間違えた人!これ凄い大問題になってますよ!と言いたいww

参考にしたサイト:
wikipedia - 波ダッシュ
Perlで日本語(ISO-2022-JP)メールを送信(まとめ)
UTF-8の「〜」と「〜」
encodeURIComponentをサーバ側で文字化けずに受信
このエントリーをはてなブックマークに追加