
PHPを使用してwebサイトの出力を行う場合「文字コード対応」は避けて通れない部分です。
大抵はUTF-8ですが、マイクロソフトオフィスとの親和性を考えるとShift-JISもありです。
PHPでHTMLを出力することを考えると「どちらの文字コードでも出力できる」が望ましいわけです。
そりゃそうです。ホームページの文字コードをUTF-8にするかShift-JISにするかは仕様で変わります。
ならば、どちらにでも対応できるようにするほうが利便性が非常に高いです。
そんな時に役に立つのが「mb_detect_encoding()関数」です。
この関数は、指定した文字列がどの文字コードなのかを調べてくれます。
文字コードに自動対応するには必須の関数です。
ところが、最近文字コード判定をしているのに、誤判定になることがありました。
Shift-JISなのに、EUC-JPと判断されたりしました。
いったいどういうこと?
と思いましたが、よくよく調べて対応することができました。
備忘録として記録いたします。
今回の現象
次のような記述をした場合に、文字コードがEUC-JPと表示されました。
<?php
var_dump(mb_detect_encoding(mb_convert_encoding("あいうえお", "Shift-JIS", "UTF-8"), "UTF-8, EUC-JP"));
?>
文字コードをShift-JISに変換し、文字コード確認をしたところ結果は「EUC-JP」となりました。
明らかに文字コードが異なっています。
この結果をうのみにすると、Shift-JISに文字コード変換すべきところを、EUC-JPに文字コード変換することになります。
結果文字化けが発生します。
何でこんなことが起こるのでしょうか?
原因は曖昧判断機能
mb_detect_encoding()関数は、デフォルトで文字コードをあいまい判断します。
例えばShift-JISとEUC-JPは文字コードが似ているため、誤判定されます。
今回も、指定文字コードにShift-JISがなかったために、一番似ているEUC-JPが文字コードとして返されたというわけです。
そもそも比較文字列に、Shift-JISを指定していなかったことが問題ですが、だからと言って別の文字コードが返ってくるのも問題です。
該当文字コードがない場合は、falseが帰ってくればエラーとして処理できます。
何とかする方法がないか調べました。
mb_detect_encoding()には第3の引数があり厳密比較指定ができます
曖昧判定を何とかする方法がないか調べてみたところ、mb_detect_encoding()関数には、曖昧判定と厳密判定を切り替える指定があることがわかりました。
具体的には以下の通りです。
mb_detect_encoding(”①調べる文字列”, “②文字コード指定”, “③判定の指定”);
今までは、①、②しか指定していませんでしたが、その場合デフォルトで曖昧判定になります。
厳密判定にするには、以下のようにコーディングします。
<?php
var_dump(mb_detect_encoding(mb_convert_encoding("あいうえお", "Shift-JIS", "UTF-8"), "UTF-8, EUC-JP", true));
?>
③にtrueを指定すると、厳密判定になります。
③にfalseを指定すると、曖昧判定になります。
③の指定を省略した場合はfalseとなり、曖昧判定になります。
上記の例だと、戻り値は「false」が返り、指定した中に該当文字コードはありません。となります。
これにより、誤った文字コードで処理されることがなくなりました。
ちなみにPHP4の時代からあった指定方法
なんとなく昔は大丈夫だったのに・・・というイメージがありましたが、曖昧判定、厳密判定の仕様は、PHP4のころからあったようです。なので、今回現象が見つかったのはたまたまと言えます。
曖昧検索は、キーワード検索で文字列を探す場合は欲しい機能ですが、文字コード判定の場合は厳密判定したいです。
なぜならば、文字コード自動変換対応するには、mb_detect_encoding()関数の戻り値(文字コード)で、文字変換をするためです。その根底が崩れた場合、文字化けが起こります。
今回も調べていて、良い勉強になりました。