[php] mb_convert_encoding と UTF-8 の誤変換問題

2007.07.27 Author: ぴ

phpでは mb_convert_encoding を使うことで、文字のエンコーディングを変更することができる。SJISを使う場合などで、5C文字(エスケープ誤認識)の絡みもあって、一時的にエンコーディングを別の文字コード体系に変換し、再度変換をかけて元の文字コード体系に戻す処理もよく行う。

このような処理を行う場合で、気をつけなくてはいけないのが UTF-8 絡みの問題である。

たとえば、「~」(U+FF5E)という文字をUTF-8からSJISやEUC-JP等のよく利用される文字コードに変換して、再度UTF-8に変換した場合、「〜」(U+301C)に変換されてしまうという問題がある。同様な誤変換を行ってしまう文字としては下記のものが挙げられる。

変換前変換後
文字コード文字コード
U+2225U+2016
- (マイナス)U+FF0DU+2212
U+FF5EU+301C
U+FFE0¢U+00A2
U+FFE1£U+00A3
U+FFE2¬U+00AC

このような誤変換を防ぐための一つの手法としてよく挙げられているのが、「sjis-win」または「eucjp-win」というエンコーディングを使って回避する方法である。

$s_in  = "~";
$s_out = mb_convert_encoding(
            mb_convert_encoding(
                $s_in,
                "sjis-win",
                "UTF-8"
            ),
            "UTF-8",
            "sjis-win"
         );
echo ($s_in == $s_out) ? "OK" : "NG";

表示結果:
OK
このようにすると、上記表に挙げた文字を mb_convert_encoding を使って変換しても、正しく元に戻すことができる。

「sjis-win」または「eucjp-win」を使えば解決?と思いきや、逆も真なりとはこのことかと思うような問題も存在する。たとえば、

$s_in  = "〜";
$s_out = mb_convert_encoding(
            mb_convert_encoding(
                $s_in,
                "sjis-win",
                "UTF-8"
            ),
            "UTF-8",
            "sjis-win"
         );
echo ($s_in == $s_out) ? "OK" : "NG";

表示結果:
NG
のように、表にあった変換ミス後の文字をsjis-winで変換すると、なぜか表にある変換前の文字列に戻ってしまう。つまるところ、sjis-winにしたところで、このような文字列が入ってきてしまえば、変換は正しく行えないということになる。

UTF-8から変換しても問題がないのは、UCS2, UCS4, UTF-7, UTF-16等のUnicodeの文字コード体系だけになってしまう。ただ、Unicode → Unicodeに変換することは、あまり意味がないので、この方法はほとんどの場合意味がない。

実際のところ、「表にある変換ミス後の文字列は、ほとんど出現することはあるまい」という勝手な根拠から、sjis-winやeucjp-winで変換をかけてあげれば、一応よいのではないかと思われる。もっと厳密にやるのであれば、あらかじめ置換しておくなり方法はあるので回避可能ではあるが、なんだかな?と言わざるを得ない話ではある。

※ CVS版だかPHP6だか何かで、直ったという話もどこかで聞きましたが…。試してないので何とも。

名古屋のWebシステム開発・ネットワーク構築会社 コネクティボへ