« 動的なHTMLソースのリアルタイム暗号化(JavaScriptソースの生成)に対応したPHPライブラリ | トップページ | auからのメールをPCで受信した際に、文末に「忞」の文字が入っている場合(文字化け解読) »

2007/12/13

PHPで、いわゆる機種依存文字の文字コード変換(EUC-JP→UTF-8)にはまる



私が作るPHPのサイトは、大概、EUC-JPを内部エンコーディングと設定して作成しています。それで今まで特に困ったことはなかった(自動応答メールなどに「㈱」「髙」などの機種依存文字が含まれていると文字化けすることはありましたが、そのような場合、そのような文字を入力するユーザーの知識不足を問題視していることが実際多かったのですが・・・。)のですが、今回は困りました。

いわゆる機種依存文字(「①」「㈱」「Ⅱ」「㌔」など)をEUC-JPからUTF-8へ変換しようとすると、該当文字が「?」(クエスチョンマーク・はてなマーク)に化けてしまいます。変換不能状態です。我ながら今更ですが、mb_convert_encoding()関数において、「eucJP-win→UTF-8」のようににするとこれらの文字変換はうまくできるようになることが分かりました(=下記サンプルコードの方法2)。

ただし、「髙」「纊」「忞」などの変換はできません。13区の特殊文字は、eucJP-winにすることで変換できましたが、「IBM拡張文字」もしくは「NEC選定IBM拡張文字」(PHPが結局、この2種類の拡張文字のどちらをどのように使い分けているのかについての考察はいつかしたいですが、今は省きます。)の文字コード変換はできず、どうしたものかと悩んでいました。

ところが、思いつきで、一旦、SJISに変換してからUTF-8に変換したらどうなるかと考えて、ただの?SJISではなく、sjis-winに変換後、さらにUTF-8に変換すると、うまくいうことが判明しました。まとめると、下記のようになりました。

方法1では、ぜんぜん駄目で、「?あいa???????アイウエオ」になりました。方法2では、13区の特殊文字はクリアできましたので、「あいa①㈱Ⅲ㌔アイウエオ」のようになりました。方法3では、「纊あいa忞寬①㈱Ⅲ㌔髙アイウエオ」のようになり、完全勝利です。

しかし、です。これは、あまりスマートではありません。何かいい方法はないものかと思索中です。

2007年12月18日22時17分ごろ追記
その後、下記のコメントに寄せていただいたmoriyamaさんのコメントがとても役に立ちました。PHPのバージョンを5.2.1以上に上げるか、パッチを適用するか、もしくは、上記私の記事の方法3のように2段階にするしかないようです。

私のやろうとしたこととは逆ですが、UTF-8の文字をEUC-JPに変換する際には、moriyamaさんが言われているように、注意事項として、
(以下、引用)
UTF-8 -> CP51932 を SJIS-win 経由で変換する場合は、SJIS-win に変換した後、IBM拡張文字をNEC選定IBM拡張文字に置換してから EUC-JP に変換する必要があると思われますのでご注意ください。
です。

このIBM拡張文字をNEC選定IBM拡張文字に置換してからというのが結構はまりがちのポイントだと思います。「はてな」にも同様の質問が先月あったようです。回答数は多いものの、本日現在は3件しか開かれていない状態で解決方法が表示させていないみたいです。

[はてな] PHPで機種依存文字の文字コード変換のやり方がわからなくて困っています。UTF-8の文字列をEUC-JPに変更する必要があるのですが「髙(はしご高)」などの機種依存文字を・・・
http://q.hatena.ne.jp/1194491169

|

« 動的なHTMLソースのリアルタイム暗号化(JavaScriptソースの生成)に対応したPHPライブラリ | トップページ | auからのメールをPCで受信した際に、文末に「忞」の文字が入っている場合(文字化け解読) »

コメント

PHP 5.2.1 以上になりますが、方法2で eucJP-win の代わりに cp51932 を使ってみてはいかがでしょうか?
5.2.1 から次のパッチがマージされています。
http://ml.php.gr.jp/pipermail/php-dev/2006-October/001346.html

eucJP-win(eucJP-ms)とcp51932の違いは次の記事をご覧ください。
http://msyk.at.webry.info/200511/article_2.html

投稿: moriyama | 2007/12/14 00:07

moriyama先生、コメントありがとうございます。

PHP5.2.5で動作確認できました。ありがとうございました。

で、ここまで教えていただいてまことに恐縮なのですが、恥のついでに教えていただきたいことがあります。

ご記入のとおり、5.2.0だと、5.2.1未満になるため、cp51932を指定すると、当然のごとく、
Warning: mb_convert_encoding() [function.mb-convert-encoding]: Illegal character encoding specified in ・・・
になります。

5.2.0で動作させるには、
php-5.1.4-060825.patchを適用するか、このパッチを適用しない場合には、私の上記・記事の方法3のように、いったんsjis-winに変換後、それをさらにUTF-8に変換するという2段階方式しかないのでしょうか?

また、5.1.4未満のPHPでは、php-5.1.4-060825.patchを適用できず、選択肢は上記・記事の方法3のような回りくどい方法しかないという理解でよいでしょうか?

投稿: 管理人 | 2007/12/14 08:46

> 5.2.0で動作させるには、
> php-5.1.4-060825.patchを適用するか、このパッチを適用しない場合には、私の上記・記事の方法3のように、いったんsjis-winに変換後、それをさらにUTF-8に変換するという2段階方式しかないのでしょうか?

パッチを適用しない、もしくは様々な理由でパッチを適用できない場合は、直接変換する方法は無いようです。

> また、5.1.4未満のPHPでは、php-5.1.4-060825.patchを適用できず、選択肢は上記・記事の方法3のような回りくどい方法しかないという理解でよいでしょうか?

php-5.1.4-060825.patch は、5.2.0 や 4.4.6 にも適用可能で legacy-encoding プロジェクトのテストツールでテストをパスする事を確認しました。
パッチを当てた後、configure を実行する前に次のコマンドを実行しておく必要があります。
./buildconf --force

ちなみに、CP51932 -> UTF-8 とは逆の変換 UTF-8 -> CP51932 を SJIS-win 経由で変換する場合は、SJIS-win に変換した後、IBM拡張文字をNEC選定IBM拡張文字に置換してから EUC-JP に変換する必要があると思われますのでご注意ください。

CP932(SJIS-win)、CP51932、CP50221 (7ビットJISコード)間の相互変換は IBM拡張文字の扱いに気をつけさえすれば比較的簡単に相互変換できるので、これらの間の変換を Pure PHP でライブラリ化しておくといいかもしれません。
そのようなライブラリを用意しておけば、CP51932 や CP50221 を UTF-8 との間で変換する必要がある際には、CP932(SJIS-win) 経由で変換すれば良くなります。

投稿: moriyama | 2007/12/16 13:52

moriyama先生、コメントありがとうございました。
大変参考になりました。

投稿: 管理人 | 2007/12/16 15:15

コメントを書く



(ウェブ上には掲載しません)


コメントは記事投稿者が公開するまで表示されません。



トラックバック


この記事へのトラックバック一覧です: PHPで、いわゆる機種依存文字の文字コード変換(EUC-JP→UTF-8)にはまる:

« 動的なHTMLソースのリアルタイム暗号化(JavaScriptソースの生成)に対応したPHPライブラリ | トップページ | auからのメールをPCで受信した際に、文末に「忞」の文字が入っている場合(文字化け解読) »