結論
-
fw3/streamsを
composer require fw3/streams
して使おう。 - 実装 にあるサンプルコードをコピーして必要なところだけ書き換えれば終わり。
- PHP7.2.0未満(下限PHP5.3.3)で解決したい場合はfw3_for_old/streamsを利用してください。(
composer require fw3_for_old/streams
) - fw3/streamsおよびfw3_for_old/streams共にPHP8.3.1まで動作確認済みのため、PHP7.2.0以上のシステムの場合はどちらを入れても変わりはありません。
- composerを使用できない環境の場合は、こちらからZIPファイルをダウンロードし、展開して任意のディレクトリにコピーしてください。
- その後、使用対象となる処理より前に
require_once sprintf('%s/src/filters_require_once.php', $path_to_copy_dir);
としてsrc/filters_require_once.php
を読み込むようにしてください。
はじめに
2024年時点において、CSVファイルは会社間データ連携によく利用されています。
かねてよりPHPでは 文字コードがShift_JIS(CP932)のCSVファイル の取り扱いに大変苦労してきました。
この問題については 過去の記事(PHPで高速・省メモリ・確実に日本語CSVを扱う方法) において 完全に解決しています。
一方、データ連携においてCSVファイルをZIPアーカイブにしてデータ連携をはかるケースもあります。
今までは\ZipArchiveクラスなどを利用して、一度テンポラリファイルを作成し、そのファイルに対してCSVの読み取り処理を行うなどしていました。
ただ、この方法ではテンポラリファイルの作成・維持・削除の管理が必要であり、手間もかかり考えることが増えるため苦痛度が上がっていました。
この記事では ZIPアーカイブ内のCSVファイル を 直接透過的 に扱い、CSVファイルを直接操作しているかのような 使用感を得られるようにします。
実装
ZIPアーカイブ内にある文字コードがShift_JIS(CP932)のCSVファイル から UTF-8 としてデータを吸い出して配列として返す。
<?php
use fw3\streams\filters\utilitys\StreamFilterSpec;
use fw3\streams\filters\utilitys\specs\StreamFilterConvertEncodingSpec;
use fw3\streams\filters\utilitys\specs\StreamFilterConvertLinefeedSpec;
$rows = StreamFilterSpec::decorateForCsv(function() use ($path_to_zip): array {
// フィルタの設定:`$path_to_zip`が示すZIPファイル内の`temp/test.csv`にアクセスする例
$spec = StreamFilterSpec::resourceZip($path_to_zip, 'temp/test.csv')->read([
StreamFilterConvertEncodingSpec::toUtf8()->fromSjisWin(),
]);
$rows = [];
$csvFile = new \SplFileObject($spec->build(), 'r+b');
$csvFile->setFlags(\SplFileObject::READ_CSV);
// ZIPストリームの場合、rewind出来ないためforeachを使用できません。
// そのため、次の例ではforを利用しています。
for (;\is_array($row = $csvFile->fgetcsv());) {
$rows[] = $row;
}
return $rows;
});
StreamFilterSpec::decorateForCsv() 内部でロカールを一時的に変更する処理を行っています。
そのため、この実装例においては \Generator を使用することができません。
もし、逐次処理を行いたいのならば、 \$rows[] = $row; の箇所に展開するのが好適です。
たったこれだけで実現可能です。
たのしいZIPアーカイブ内CSVファイルからのデータすいだし。
おわり。