3
2

More than 1 year has passed since last update.

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

Last updated at Posted at 2020-09-12

結論

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を読み込むようにしてください。

2022/01/20追記:PHP8.1における Sigh. Double sigh. な修正について。

PHP8.1になって突然SJIS-winが削除されました。
https://github.com/php/php-src/commit/e2459857afdf70ed4bc23bc7f614e9f383eb3072

そのため、日本語PHPにおける鉄板エンコーディング設定が使えなくなっています。

この記事で紹介しているfw3/streamsおよび、fw3_for_old/streamsでは「PHP8.1.*かつSJIS-winが指定された場合はCP932に読み替える」対応を行い、実質的な影響が出ないようにしています。

読み込み

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

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 元のコード
});
3
2
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2