Edited at

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

More than 1 year has passed since last update.

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を使ってみるのもありかもしれない。