Webアプリケーションの案件には、結果をCSVで出力したいと言う要望はよくあります。
その要望につきまとうのが、Excelでみたときに文字化けする問題です。
さて、
今回は、この問題の解決方法について触れます。
また、ファイルの出力はLaravelのStorage機能を使用した場合を想定した解説です。
イントロダクション
この記事で得られる事
- PHPで保存するファイルに、BOMファイルを付与する方法がわかる。
- Laravelの場合の対応方法がわかる。
なぜ文字化けするのか
ExcelのデフォルトエンコードはShift-JISだから文字化けを起こします。
そのため、Excel側で読み込む際に文字コードを変えてあげたり、CSVをテキストエディタで開いて文字コードを変えてあげれば正しく表示出来ます。
しかし、その作業は手間と考える人が多く、それはバグだと思われるケースの方が圧倒的に多いです。
もちろん、最初からShift-JISを使う、もしくは、出力時にUTF-8からShift-JISに変換すると言う方法もあります。
しかし、UTF-8の方がサポートしている文字は多いです。
直近で問題が無くても、運用していいくうちに恐らく何処かで問題が生じます。
ExcelでUTF-8のファイルを文字化けせずに開く方法
ExcelでUTF-8と認識させる方法として、BOM付きUTF-8で保存すると言う方法があります。
この形式で保存されたデータであれば、ExcelでおUTF-8だと認識してくれます。
基本
ファイルのheader情報で指定する方法もあります。
手っ取り早い方法は、ファイルに書き出すときに先頭に追加してあげます。
file_put_contents(<UTF-8のファイル>, "\xEF\xBB\xBF". $content);
この際、BOMのコードは必ずダブルコーテーションで指定してください。
本題
応用してStorageに保存する方法に解説します。
次のコードが、配列をCSVの形式に変換してストレージに保存する処理です。
use Storage; use League\Csv\Writer; class makeCsv { function publishStorage() { $header = [ 'code', 'name', 'address' ]; $record = [ [ 'code' => 1, 'name' => '太郎', 'address' => '東京都', ], [ 'code' => 2, 'name' => '二郎', 'address' => '京都府', ], [ 'code' => 3, 'name' => '三郎', 'address' => '北海道', ] ]; $csv = Writer::createFromString(new \SplTempFileObject()); $csv->insertOne($header); $csv->insertAll($records); Storage::disk(<disk名>)->put(<出力先>, "\xEF\xBB\xBF" . $csv->__toString()); } }
以上です。
尚、今回作成するテストコードでは、CSVのコンバート用にLeague\Csvを使用しています。
もし、このコードを参考にされる場合は、ライブラリの設定をお願いします。
解説
Storageに保存する処理は次の部分です。
出力するためのput
の引数に、BOM付きの文字を追加します。
Storage::disk(<disk名>)->put(<出力先>, "\xEF\xBB\xBF" . $csv->__toString());
実際にput
はどういった処理をしているかというと下記です。
public function put($path, $contents, $lock = false) { return file_put_contents($path, $contents, $lock ? LOCK_EX : 0); }
そのため、file_put_contents
と同じ要領で指定すれば対応できます。