【PHP】多言語のデータをCSVに出力する

多言語のテキストを含むデータをCSVファイルに出力して、さらにエクセルで開いて編集したいという要件があったのでその時の対応メモです。

多言語データの処理

データ

データのイメージとしては以下です。配列に読み込まれた様々な言語のデータをファイルに出力する際の例です。

$data = [
    array(
        'id' => 1,
        'name_ja-JP' => 'テスト',
        'name_en-US' => 'test',
        'name_zh-CN' => '测验',
        'name_ko-KR' => '테스트',
    ),
    array(
        'id' => 2,
        'name_ja-JP' => '茄子',
        'name_en-US' => 'eggplant',
        'name_zh-CN' => '雄狮',
        'name_ko-KR' => '가지',
    ),
];

NGケース

ファイル出力処理

最初は以下のようなコードでした。

エクセルで編集という要件から、Shift-JISに変換して出力しています。

$handle = fopen('data.csv', 'w');
foreach($data as $row){
    array_walk($row, function(&$item){
        $item = mb_convert_encoding($item, 'Shift-JIS', 'UTF8');
    });
    fputcsv($handle, $row);
}
fclose($handle);

エクセルで開く

日本語以外のマルチバイト文字列が文字化けします。

Shift-JISで出力しているわけですから当然といえば当然です。

OKケース

ファイル出力処理

以下のように対応しました。

$buff = pack('C*',0xEF,0xBB,0xBF);
foreach($data as $row){
    $buff .= implode(',', $row)."\n";
}
file_put_contents('data.csv', $buff);

ファイルの先頭にBOM(Byte Order Mark)と呼ばれるおまじないを出力します。

これは、どの形式の符号化形式を採用しているかなどの情報をもつ16ビットの値です。

UTF-8の場合は先頭の3ビットがそれにあたり、0xEF 0xBB 0xBF というデータになります。

したがって文字コードはUTF-8で出力するのが大前提で、それ以外だった場合はUTF-8への変換が必要です。

エクセルで開く

ちなみにこの方法だと絵文字や記号も出力できます。

BOM付きのCSVファイルを読み込む時の注意

読み込むときは注意が必要で、BOMを除外しないと正しく読み込めない可能性があります。

※先頭がヘッダー行とかだったら無視してもいいかもしれません。

  $count = 0;
  while (($data = fgetcsv($handle, 4096, ",")) !== FALSE) {
      if ($count == 0) {
          // BOM削除
          $data[0] = trim(preg_replace('/^\xEF\xBB\xBF/', '', $data[0]), '"');
      }
      $count++;

上記のコードは正規表現で先頭のBOMを削除しています。

また、エンクロージャー付きのCSVファイルの場合、BOMを読み込んだカラムに限りエンクロージャーごと取り込んでしまうので、trimしてやると良いです。

上記コードの例だと変数 $data[0] にこんな感じで入っています。

<BOM>"1"

備考

Shift-JISとUTF-8の特徴

Shift-JIS

消費バイト数が比較的少ない。

表現できる文字種は日本語を含む9000種ほど

UTF-8

文字範囲が広く、どんな言語の文字でも文字化けしない(世界標準)

容量はShift-JISの約1.5倍

コメント

このブログの人気の投稿

docker-compose up で proxyconnect tcp: dial tcp: lookup proxy.example.com: no such host

docker-compose で起動したweb、MySQLに接続できない事象

【PHP】PHP_CodeSnifferを使う(コーディングルールのカスタマイズ)