LoginSignup
28

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-08-07

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

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
28