0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PHPでZIPファイル内のCSVファイルをそのまま読み取る

Posted at

結論

  • fw3/streamscomposer 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
<?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ファイルからのデータすいだし。
おわり。

0
0
0

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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?