PHPでShift-JISにエンコードしたCSVをダウンロードする機能を実装したのですが、日本語の文字化けが発生。
原因は「PHPファイルの文字コード」でした。
なかなかこの原因にたどり着けずに、解決時間がかかってしまいました。
解決方法のみ知りたい方の為に先に解決方法を。
では、事象の内容と解決方法を詳しく解説していきます。
【発生事象】Shift-JISに変換しているCSVのダウンロード処理でのみ文字化けする
まずは、どのような状況で発生したのか整理します。
文字化けするのはダウンロードするCSVのみです。
通常のWebページは文字化けせず正常に表示されています。
文字コードは、通常のページは全てUTF-8で、CSVダウンロードの時のみ、mb_convert_encodingでShift-JISにエンコードしています。
エンコードのソースコード(調査の結果このソースコードには問題は無いことがわかりました)
mb_convert_encoding($str, 'sjis', 'utf-8');
このPHPシステムと全く同じものを、他のPCやサーバーでも動かしていますが、この事象が発生するのは、新規にセットアップした1台のPCでのみ発生しています。
ソースコードも同じですし、php.iniの文字コード関連も見直しましたが改善しませんでした。
【調査】実はShift-JISのエンコードは出来ていたがBOMが付いている
実際に行った調査 の内容について。
Shift-JISへのエンコードは出来ている
文字コードを指定できるツールで文字化けしているCSVをShift-JISで開いてみる事に。
IE(Internet Explorer)で簡単に文字コードを指定して開けます。
csvの拡張子を、txtに変更してIEにドラッグ&ドロップしてみます。
(csv拡張子のままだと、素直に開いてくれませんでした。)
開けたら画面を右クリック、「エンコード」→「日本語(シフトJIS)」とクリックすればOK。
この方法でShift-JISで開いたら、文字化けせずに表示されました。
ということは、Shift-JISのエンコードは問題ないのに、なぜEXCELなどで開くと文字化けするのか…
BOMが付いている!
そこで、SublimeTextでファイルを開いてみました。
Shift-JIFにエンコードしているはずなのに、なぜかUTF-8 with BOMとして認識されていました。
【原因】BOMヘッダが付加されてUTF-8と認識される
どうやら大概のツールは文字コードを判定する際にBOMヘッダが付加されていると実際の文字コードに関係なくUTF-8で開くようです。
Shift-JISにエンコードしているのに、UTF-8で開いていたら文字化けして当然ですね。
このCSVは、Shift-JISなのにBOMが付いているから、ツールがUTF-8と勘違いして文字化けして表示しているって事です。
これはなかなか気づきませんね。
普段BOMなんて気にしてませんし、なんでBOM付いているのか謎ですしね。
【解決方法】PHPファイルをBOM無しのUTF8に変換する事で解決
ではなんでBOM付きのShift-JISのファイルが出来上がってしまったのでしょうか?
PHPは、「ソースファイルがBOM付きだとechoした結果にもBOMが付く」のです。
require_onceしたファイルのどれかにBOMがあっても同様です。
ようするに。
Shift-JISにエンコードしているが、PHPファイルがBOM付だと、BOM付きのShift-JISのCSVを出力するんです。
ツールは「BOMが付いてるからUTF-8」と判断して、文字化け発生するという流れです。
PHPファイルをBOM無しのUTF-8でしていれば、基本的にこの事象は発生しません。
もちろん、今回のプロジェクトでもBOMありで開発していたわけではないんです。
なのに、なぜ事象が発生したのか。
今回は新しい環境にモジュールをセットアップしたのですが、その時に環境依存のconfig系のPHPをさくっとメモ帳で修正していました。
メモ帳はUTF-8をBOMありでしか保存出来ないのです。
なので、メモ帳で修正を保存した時に、BOMが勝手に追加されていたのでした。
という事で、そのPHPファイルをBOM無しのUTF-8に変換する事で、文字化けは解決しました。
まとめ:PHPファイルをメモ帳で修正しない
PHPファイルをメモ帳で更新したことが原因でした。
今回の問題は原因の特定にかなり時間がかかってしまいました。
メモ帳ではPHPのファイルを修正しないようにしましょう。
この記事が少しでも参考になれば幸いです。
最後までお読み頂きありがとうございました。