PHP
オブジェクト指向
CSV
SplFileObject

PHP:CSVファイルをSplFileObjectでオブジェクティブに読み込む

PHP5.1.0以上の環境では、CSVファイルを読み込むためのSplFileObjectクラスが用意されている。
基本的には以下の参考元と変わらないが、いくつかの方法を試してみる。

参考:【PHP】CSVファイルの読み込み

まずはCSVファイルを用意する

今回は、1行目にカラム名が含まれるものを用意した。
以下では、カラム名を除いて値を参照する方法を考えたい。

people.csv
name,hometown,age,job
Alice,London,25,teacher
Philipp,Geneva,33,economist
Taro,Tokyo,55,doctor

ケース1:シンプルに値を取得

test1.php
$fp = new SplFileObject('people.csvへのpath');
$fp->setFlags(SplFileObject::READ_CSV);

foreach ($fp as $line) {
    var_dump($line);
}
test1.phpの実行結果
array(4) {
  [0]=>
  string(4) "name"
  [1]=>
  string(8) "hometown"
  [2]=>
  string(3) "age"
  [3]=>
  string(3) "job"
}
array(4) {
  [0]=>
  string(5) "Alice"
  [1]=>
  string(6) "London"
  [2]=>
  string(2) "25"
  [3]=>
  string(7) "teacher"
}
array(4) {
  [0]=>
  string(7) "Philipp"
  [1]=>
  string(6) "Geneva"
  [2]=>
  string(2) "33"
  [3]=>
  string(9) "economist"
}
array(4) {
  [0]=>
  string(4) "Taro"
  [1]=>
  string(5) "Tokyo"
  [2]=>
  string(2) "55"
  [3]=>
  string(6) "doctor"
}
array(1) {
  [0]=>
  NULL
}

伝統的(原始的)なfopenとfgetを用いるコードより圧倒的に綺麗だが、カラム名と空白行が取得されてしまう。

ケース2:seekメソッドを用いてみる

SplFileObject::seek — ファイルポインタを指定行に移動させる

seekメソッドのパラメータは

ゼロを起点とした移動させる行数

らしいので、1を入れてみる。
ただし、いくつかメソッドを使う。
SplFileObject::current — ファイルの現在の行を取得する
SplFileObject::eof — ファイルの終端に到達しているか調べる
SplFileObject::next — 次の行を読み出す

test2.php
$fp = new SplFileObject('people.csvへのpath');
$fp->setFlags(SplFileObject::READ_CSV);
$fp->seek(1);

while (! $fp->eof()) {
    var_dump($fp->current());
    $fp->next();
}
test2.phpの実行結果
array(4) {
  [0]=>
  string(5) "Alice"
  [1]=>
  string(6) "London"
  [2]=>
  string(2) "25"
  [3]=>
  string(7) "teacher"
}
array(4) {
  [0]=>
  string(7) "Philipp"
  [1]=>
  string(6) "Geneva"
  [2]=>
  string(2) "33"
  [3]=>
  string(9) "economist"
}
array(4) {
  [0]=>
  string(4) "Taro"
  [1]=>
  string(5) "Tokyo"
  [2]=>
  string(2) "55"
  [3]=>
  string(6) "doctor"
}
array(1) {
  [0]=>
  NULL
}

空白行が残る。。。
ポインタの問題か。。。

ケース3:foreachに再帰する

ただし、
SplFileObject::key — 行番号を取得する
を使って、最初の行だけ除くよう処理する。

test3.php
$fp = new SplFileObject('people.csvへのpath');
$fp->setFlags(SplFileObject::READ_CSV);

foreach ($fp as $line) {
    if ($fp->key() > 0 && ! $fp->eof()) {
        var_dump($line);
    }
}
test3.phpの実行結果
array(4) {
  [0]=>
  string(5) "Alice"
  [1]=>
  string(6) "London"
  [2]=>
  string(2) "25"
  [3]=>
  string(7) "teacher"
}
array(4) {
  [0]=>
  string(7) "Philipp"
  [1]=>
  string(6) "Geneva"
  [2]=>
  string(2) "33"
  [3]=>
  string(9) "economist"
}
array(4) {
  [0]=>
  string(4) "Taro"
  [1]=>
  string(5) "Tokyo"
  [2]=>
  string(2) "55"
  [3]=>
  string(6) "doctor"
}

おそらく、このコードが一番見やすく綺麗である。

まとめ

カラム名と空白行を除いた上で値を取得するには、ケース3がいいと思われる。
しかし、膨大な行数のファイルを読み込むときには、その行数と同じだけのif処理が行われることになる。
したがって、値を取得して何かの処理を行う際に、速度だけを意識すればいいときはケース1を使ってみるのもありかもしれない。