PHPでUTF-8のCSVファイルをエクセルで文字化けせずに開けるようにする
PHPでCSV出力を組むことはよくあります。
レポートデータの出力などです。
その際にUTF-8でCSV出力したものをエクセルで開くと文字化けします。
エクセルの標準文字コードがShift-JISだからです。
ならば、Shift-JISでCSV出力すればエクセルで文字化けせずに開けますが、今度は違う問題が発生します。それは特定の特殊文字(旧字などのShift-JISに対応していない文字)が文字コードのまま出力されてしまいます。
まさに、「帯に短し、襷に長し」状態です。
CSV出力なので、文字コードで出力されるのはNGです。
HTMLならば、文字コードでも文字として表示されますが、CSVの場合数字の羅列で読めなくなるためです。
なので、CSV出力はUTF-8 一択になります。
今回はUTF-8で出力されたCSVを文字化けさせずにエクセルで開く方法を調べてみました。
現象はこのような感じです。
UTF-8で出力したCSVをエクセルで開いたら文字化けしました。
マイクロソフト製品はShift-JISなので、UTF-8の文字が無理やりShift-JISで表示された結果です。
何とかするにはCSVファイルに「UTF-8」であることを記述する
いろいろググって調べてみると、エクセルでもUTF-8のCSVを文字化けさせずに開かせる方法がありました。それがこちらです。
- CSVファイルに「このファイルはUTF-8で書かれています」という情報を付加する(BOMコード付加)
実際にやってみるとUTF-8のCSVが文字化けせずにエクセルで開けました。
エクセル側の細工は不要なので、汎用性の高い方法です。
これは良いと思いましたが、さっそく壁にぶつかりました。
何をどうしたらそうなるのかさっぱりわかりません。
ということでさらに調べてみました。
UTF-8のCSVファイルが文字化けせずに開ける理屈
まず理屈について調べてみました。
エクセルは基本Shift-JISでないと文字化けします。
ただ「このファイルはUTF-8で書かれています」と指定してあれば、UTF-8のCSVでも文字化けせずに開くことができます。
CSVファイルにUTF-8であることを指定するのが「BOM」と呼ばれるコードです。
イメージ的にはBOMコードをCSVファイルの先頭に埋め込みます。
CSVにBOMコードを埋め込む具体的な方法
CSVにBOMコードを埋め込めばよいことがわかりましたが、具体的にはどうすればよいのでしょうか?
ということでBOMコードを埋め込むサンプルがこちらです。
1 2 3 4 5 6 |
<?php $csv_out = array("あいうえお\nかきくけこ\nさしすせそ"); $csv_out = pack("C*",0xEF,0xBB,0xBF).$csv_out; // $csv_outをファイル出力する ?> |
早速よくわからないものが多いです。
一つずつ解説します。
0xEF,0xBB,0xBFって何?
これはUTF-8のBOMコードです。
UTF-8を明示するコードなので、固定値になります。
“C*”って何?
これはpack関数のフォーマット指定になります。
この例だとC(符号なし文字指定)を*(最後の文字まで指定)になります。
BOMコードがバイナリで3文字(0xEF,0xBB,0xBF)あり、この3文字に符号なし文字の指定をしていることになります。
別の書き方をすれば「”CCC”」となります。
そもそもpack関数は何をするものなの?
文字列をバイナリコードとして変換・結合する関数です。
0xEF,0xBB,0xBFはバイナリの文字コードを指定していますが、次のように記述した場合、ただの文字列として出力されます。
1 2 3 |
<?php $csv_out = "0xEF"."0xBB"."0xBF"; ?> |
エクセルは、バイナリコードで判別するため、文字列ではなくバイナリコードを埋め込む必要があります。ただの文字列をバイナリコードとして変換するのがpack関数です。
1 2 3 |
<?php $csv_out = pack("C*",0xEF,0xBB,0xBF); ?> |
バイナリとはそもそも何?
バイナリは数字で表現されるコードをさします。
プログラムは全て数字で表現されます。
10進数であれば、0~9
2進数であれば、0~1
16進数であれば、0x0~0xF
数字なのにアルファベットがあるじゃないか?
と疑問に感じられますが、これはプログラムをするうえで便宜上「文字」として表現しているだけなのです。
実際に動作する場合にシステムが認識できるコードに変換することで動作します。
このシステムが認識できるコードがバイナリコードです。
なんとも禅問答のような内容ですが、PHPで記述している内容は「文字」であり「バイナリ」ではありません。バイナリコードの表現をPHPで書いたとしても、それは「文字」なのです。
なので、システムが認識できるコードに変換しているのです。
ちなみにこのやり方はWindowsのエクセル用です。
いろいろ書いておいてあれですが、今回の内容はWindowsのエクセルでのみ動作します。
マッキントッシュのエクセルだと正しく動作しません。
マックのエクセルでも文字化けせずに開く方法はありますが、マックのエクセルがなく、検証できないためWindows用エクセルで開く方法のみについて書きました。
ググって見つけたやり方を転載すればよいのかもしれませんが、検証していない内容は書きたくないためご容赦いただければ幸いです。