🐥

PHPで文字化けするCSV処理を文字化けしなくする方法

2021/01/30に公開

結論

fw3/streamscomposer require fw3/streamsして、CSVを扱っている処理を次のように囲ってあげれば文字化けしなくなります。

※ PHP7.2.0未満(下限PHP5.3.3)で解決したい場合はfw3_for_old/streamsを利用してください。(composer require fw3_for_old/streams
  fw3/streamsおよびfw3_for_old/streams共にPHP8.0 beta 2まで動作確認済みのため、PHP7.2.0以上のシステムの場合はどちらを入れても変わりはありません。
※ composerを使用できない環境の場合は、こちらからZIPファイルをダウンロードし、展開して任意のディレクトリにコピーしてください。
  その後、使用対象となる処理より前にrequire_once sprintf('%s/src/filters_require_once.php', $path_to_copy_dir);としてsrc/filters_require_once.phpを読み込むようにしてください。

読み込み

既存の実装が次のような形の場合。

php

$fp     = \fopen($csv_file_path, 'r+b');
$data   = [];
while (($row = \fgetcsv($fp, 1024)) !== FALSE) {
    $data[] = $row;
}
\fclose($fp);

次のようにラップしてあげるだけで解決。

php
<?php

// fw3_for_oldを使用している場合は、`use fw3\`を`use fw3_for_old\` として読み替えてください。
use fw3\streams\filters\utilitys\StreamFilterSpec;
use fw3\streams\filters\utilitys\specs\StreamFilterConvertEncodingSpec;
use fw3\streams\filters\utilitys\specs\StreamFilterConvertLinefeedSpec;

$data   = StreamFilterSpec::decorateForCsv(function () use ($csv_file_path) {
    // フィルタの設定
    $spec   = StreamFilterSpec::resource($csv_file_path)->read([
        StreamFilterConvertEncodingSpec::toUtf8()->fromSjisWin(), // Shift_JIS(Windows-31J、MS932)として読み込んでUTF-8として出力
    ]);

//// STA 元のコード
//
    $fp     = \fopen($spec->build(), 'r+b'); // ここだけ `$csv_file_path` を `$spec->build()`に書き換える
    $data   = [];
    while (($row = \fgetcsv($fp, 1024)) !== FALSE) {
        $data[] = $row;
    }
    \fclose($fp);
//
//// END 元のコード

    return $data;
});

書き込み

既存の実装が次のような形の場合。

php
$fp = \fopen($csv_file_path, 'w+b');
foreach ($rows as $row) {
    \fputcsv($fp, $row);
}
\fclose($fp);

次のようにラップしてあげるだけで解決。

php
<?php

// fw3_for_oldを使用している場合は、`use fw3\`を`use fw3_for_old\` として読み替えてください。
use fw3\streams\filters\utilitys\StreamFilterSpec;
use fw3\streams\filters\utilitys\specs\StreamFilterConvertEncodingSpec;
use fw3\streams\filters\utilitys\specs\StreamFilterConvertLinefeedSpec;

$data   = StreamFilterSpec::decorateForCsv(function () use ($csv_file_path, $rows) {
    // フィルタの設定
    $spec   = StreamFilterSpec::resource($csv_file_path)->write(array(
        StreamFilterConvertEncodingSpec::toSjisWin()->fromUtf8(),   // UTF-8として読み込んでShift_JIS(Windows-31J、MS932)として出力
        StreamFilterConvertLinefeedSpec::toCrLf()->fromAll(),       // いかなる改行コードであってもCRLFとして出力
    ));

//// STA 元のコード
//
    $fp = \fopen($spec->build(), 'w+b');
    foreach ($rows as $row) {
        \fputcsv($fp, $row);
    }
    \fclose($fp);
//
//// END 元のコード
});

Discussion