要件
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ライフを!