要件
PHPでCSVを読み込む方法はネット上でたくさん見つかりますが、文字コードの変換のために一旦別ファイルに保存したり、読み込んだデータにアクセスしにくかったりで、何度書いてもなんとなくスッキリしてませんでした。もっといい方法があるんじゃないかなーって。 で、今回満足のいくCSVローダーが書けたので、ご紹介します。
要件は以下の通り。
- CSVデータ中に
"
や改行が入っている場合にも正しく対応したい。 - 変数に読み込まれたデータは文字コードUTF-8にすること。
- 文字コードの変換の際に一時ファイルを作らないで済むようにしたい。
- 各レコードの列にアクセスするのに、カラム列番号じゃなくてCSV1行目のヘッダ文字列をキーにしたい。
- 数万行くらいのデータならさくっと読み込んで欲しい。
- PHP5.3でも動かしたい。
CSV読み込みPHP関数
ということで、PHPフィルタ(php://filter) と SplFileObject を使用してPHP関数を作成しました。
<?php /** * CSVローダー * * @param string $csvfile CSVファイルパス * @param string $mode `sjis` ならShift-JISでカンマ区切り、 `utf16` ならUTF-16LEでタブ区切りのCSVを読む。'utf8'なら文字コード変換しないでカンマ区切り。 * @return array ヘッダ列をキーとした配列を返す */ function get_csv($csvfile, $mode='sjis') { // ファイル存在確認 if(!file_exists($csvfile)) return false; // 文字コードを変換しながら読み込めるようにPHPフィルタを定義 if($mode === 'sjis') $filter = 'php://filter/read=convert.iconv.cp932%2Futf-8/resource='.$csvfile; else if($mode === 'utf16') $filter = 'php://filter/read=convert.iconv.utf-16%2Futf-8/resource='.$csvfile; else if($mode === 'utf8') $filter = $csvfile; // SplFileObject()を使用してCSVロード $file = new SplFileObject($filter); if($mode === 'utf16') $file->setCsvControl("\t"); $file->setFlags( SplFileObject::READ_CSV | SplFileObject::SKIP_EMPTY | SplFileObject::READ_AHEAD ); // 各行を処理 $records = array(); foreach ($file as $i => $row) { // 1行目はキーヘッダ行として取り込み if($i===0) { foreach($row as $j => $col) $colbook[$j] = $col; continue; } // 2行目以降はデータ行として取り込み $line = array(); foreach($colbook as $j=>$col) $line[$colbook[$j]] = @$row[$j]; $records[] = $line; } return $records; } ?>
【更新】2018/1/9 16:12
上記プログラムの26行目に SplFileObject::DROP_NEW_LINE
があったのですが、これがあると、複数行入っているセルの1行目の改行コードまで除去されてしまうため、削除しました。
使い方と実行サンプルの画面キャプチャ
以下のように使います。
<?php $records = get_csv('data.csv'); var_dump($records); ?>
以下、実行サンプルです。
このような感じで配列に変換されますので、 $records[0]['名前']
等で直接データにアクセス出来ますし、この形なら array_multisort()
や usort()
を使って特定のキーでのソートもラクショーですね!
ちなみに、UTF-8版やUTF-16版のCSVを読み込みたい場合は、 get_csv()
関数の第二引数の指定が必須です。(UTF-16版のCSVのみ、区切り文字はカンマ ,
ではなくタブ \t
になります)
$records = get_csv('data.csv'); // Shift-JISのCSV(カンマ区切り) $records = get_csv('data.csv', 'utf8'); // UTF-8のCSV(カンマ区切り) $records = get_csv('data.csv', 'utf16'); // UTF-16のCSV(タブ区切り)
また、約3万行のCSVデータ(約2MB)でテストしたところ、2010年発売のMacBookAir(Core2Duo メモリ4GB)上のDockerコンテナ(VirtualBox)上で1.5秒くらいの速度が出てますので、最近の一般的なサーバなら1秒かからないでしょう。とは言ってもサーバプランや回線環境にも依存するものと思いますので、速度が重要なら事前にテストをしてください。
以上、ハッピーCSVライフを!