PHPのSplFileObjectでファイル読み込み!(テキスト・CSV)

PHP

PHPでSplFileObjectを使って、ファイルを読み込む方法について書いています。
最初にSplFileObjectクラスについて簡単に解説しています。
その後にテキストファイルとCSVファイルを読み込んで、挙動を確認してみました。

載せているサンプルコードについては、PHPのバージョン8.1.8を使って検証しています。

SplFileObjectについて

SplFileObjectのSplはStandard PHP Libraryの略称です。

SplFileObject以外にもPHPで使えるライブラリがいろいろあります。
Standard PHP Libraryの他のライブラリについては、公式ドキュメントのこちらに書いています。

SplFileObjectはファイル操作のためのクラスです。

SplFileObjectを使うことで、簡単にファイルを読み込めます。
PHPで最初から使えるライブラリなので、Laravelのコントローラーなどでも使うことが可能です。
CSVファイルを読むための設定もあるので、CSVファイルについても簡単に読み込むことができます。

テキストファイルを読み込む

SplFileObjectを使って、テキストファイルを読み込んでみます。
ファイルを読み込むプログラムを下記のように作成しました。

<?php
$file = new SplFileObject("hello.txt");

foreach ($file as $number => $text) {
    echo "{$number}: {$text}";
}

解説

最初にSplFileObjectクラスをnewして、ファイルを読み込んでいます。
SplFileObjectのコンストラクタに引数で渡しているのはファイルのパスになります。(“hello.txt”)

これだけで、ファイルの読み込みが完了して、$file変数に読み込んだファイルの情報が入ります。
foreach文でループすることで、ファイルの内容を1行ずつ取り出して、処理することが可能です。

今回は$file変数をforeach文に指定しています。
$number変数が0からの行番号、$text変数がファイルの内容になります。
echoで行番号とテキストの内容を出力するようにしました。

動作確認

今回は、下記のファイルを読み込んで出力してみます。

PHPのSplFileObjectのテスト
テキストファイルを読み込む
Hello, World!

先ほどのプログラムを実行します。
そうすると、下記のように行番号とファイルの内容が表示されました。

0: PHPのSplFileObjectのテスト
1: テキストファイルを読み込む
2: Hello, World!

ファイルの行数を取得したい

ファイルが行数を取得したい場合には、下記のように最終行の位置まで移動して、keyメソッドを呼びます。

<?php
$file = new SplFileObject("hello.txt");

$file->seek(PHP_INT_MAX);
$count = $file->key() + 1;
echo $count; // 3

$file変数にSplFileObjectのインスタンスを作成して、格納しています。

次の処理で、インスタンスからseekメソッドを呼んで、引数で指定した個所にファイルポインタを移動させています。
引数で指定しているのはPHP_INT_MAXです。これはPHPがサポートしている整数型の最大値になります。

これで、ファイルの最終行に移動して、keyメソッドを呼ぶことで行番号を取得しています。
行番号は0からなので、+1することで最終行の行番号になります。
今回はファイルの行数が3行なので、「3」が返ってきました。

ファイル名を取得したい

ファイル名を取得したい時には、getFilenameメソッドを呼びます。

<?php
$file = new SplFileObject("/Users/xxx/test/hello.txt");
echo $file->getFilename(); // hello.txt

最初に絶対パスで指定して、SplFileObjectのインスタンスを$fileに格納しています。
次の行でgetFilenameメソッドを呼んでいます。
これで、ファイルの名前を取得することができます。

拡張子と名前を分けたい場合には、下記のようにexplode関数を使うと良いです。

<?php
$file = new SplFileObject("/Users/xxx/test/hello.txt");

$fileNames = explode('.', $file->getFilename());
echo $fileNames[0]; // hello
echo $fileNames[1]; // txt

CSVファイルを読み込む

SplFileObjectを使って、CSVファイルを読み込んでみます。
setFlagsメソッドで、CSVファイルを読む設定をすることで、読み込みが簡単にできます。
setFlagsメソッドについては、公式ドキュメントのこのあたりに書いています。

$file = new SplFileObject("test.csv");
$file->setFlags(SplFileObject::READ_CSV);

foreach ($file as $row) {
    list($fruit, $color, $price) = $row;
    echo "{$fruit}は{$color}で{$price}円です\n";
}

先ほどと同様に、SplFileObjectクラスをnewして、ファイルを読み込んでいます。
SplFileObjectには、読み込むファイルのパスを渡しています。(“test.txt”)

その次の行で、setFlagsメソッドを利用して、CSVを読み込む設定を行なっています。
定数のSplFileObject::READ_CSVを渡すことで、CSV読み込みの設定が完了します。
これでCSVファイルを読み込み時に、カンマやダブルクォーテーションなどを解析して、配列で読み込んでくれます。

foreach文で$file変数を元にループしています。
$row変数に、CSVファイルの1行のそれぞれの値が配列で格納されます。

そしてlist関数を使って、$row変数の0番目〜2番目の値を受け取っています。
CSVカラムの0番目が$fruit、1番目が$color、2番目が$price変数に設定されます。

echoで内容を出力して処理が終わります。

動作確認

動作確認をしてみます。今回は下記のようにCSVを用意しました。

"りんご", "赤", "100"
"ぶどう", "青", "150"
"バナナ", "黄", "170"

実行すると、このように配列として値が受け取れて、表示されることが確認できました。

りんごは赤で100円です
ぶどうは青で150円です
バナナは黄で170円です

ダブルクォーテーションで囲まれてないときには?

ダブルクォーテーションでCSVの値が囲まれていない場合でも読み込み可能です。
しかし、カンマの後に空白があると、値の空白として処理されるので注意が必要です。

例えば、下記のようなCSVの場合です。

りんご, 赤,100
ぶどう, 青,150
バナナ, 黄,170

先ほどのプログラムで出力すると、このようになります。

りんごは 赤で100円です
ぶどうは 青で150円です
バナナは 黄で170円です

CSVカラムの2番目の先頭に空白があるので、空白も値としてみなされてしまいました。
このような場合はtrim関数を使うと良いかもしれません。
参考:PHPで空白を消すtrim関数の使い方!(trim, ltrim, rtrim)

タブ区切りCSV(TSV)の場合はどうなる?

タブ区切りのTSVと呼ばれる形式の場合には、setCsvControlメソッドを使うと良いです。
setCsvControlメソッドでは、CSVの区切り文字や囲み文字、エスケープ文字が指定できます。
詳細は公式のこちらに記載があります。

例えば、下記のように先ほどのプログラムを修正します。

$file = new SplFileObject("test.csv");
$file->setFlags(SplFileObject::READ_CSV);
$file->setCsvControl("\t");
// --- 省略 ---

上記では、setFlagsメソッドでCSVを指定した後に、setCsvControlメソッドでタブ文字(“\t”)を指定しています。
こうすることで、タブ区切りのTSVファイルに対応することが可能です。

このようなTSVファイルの場合でも…

"りんご"   "赤" "100"
"ぶどう"   "青" "150"
"バナナ"   "黄" "170"

先ほどと同じように、下記の出力になります。

りんごは赤で100円です
ぶどうは青で150円です
バナナは黄で170円です

空行を読み飛ばす

setFlagsメソッドに定数を設定することで、CSVに空行があったときに読み飛ばすことが可能です。
例えば、下記のように空行があるCSVファイルの場合です。

"りんご", "赤", "100"
"ぶどう", "青", "150"

"バナナ", "黄", "170"

SplFileObject::READ_CSVのみを設定した下記のようなプログラムだとします。

$file = new SplFileObject("test.csv");
$file->setFlags(SplFileObject::READ_CSV);

foreach ($file as $row) {
    var_dump($row);
}

var_dump関数で、読み込んだカラム情報を出力しています。
そうすると、このように、空白行も1行として読み込まれてしまいます。

array(3) {
  [0]=>
  string(9) "りんご"
  [1]=>
  string(3) "赤"
  [2]=>
  string(3) "100"
}
array(3) {
  [0]=>
  string(9) "ぶどう"
  [1]=>
  string(3) "青"
  [2]=>
  string(3) "150"
}
array(1) {
  [0]=>
  NULL
}
array(3) {
  [0]=>
  string(9) "バナナ"
  [1]=>
  string(3) "黄"
  [2]=>
  string(3) "170"
}
array(1) {
  [0]=>
  NULL
}

setFlagsメソッドに定数を追加することで、空白行をスキップして処理することが可能です。
SplFileObject::SKIP_EMPTYSplFileObject::DROP_NEW_LINESplFileObject::READ_AHEADの設定を追加しています。
そうすると、空行をスキップして読み込んでくれます。

$file->setFlags(
            SplFileObject::READ_CSV |
            SplFileObject::SKIP_EMPTY |
            SplFileObject::DROP_NEW_LINE |
            SplFileObject::READ_AHEAD
        );
// --- 省略 ---

実行すると、このように空行がスキップされ、読み込まれました。

array(3) {
  [0]=>
  string(9) "りんご"
  [1]=>
  string(3) "赤"
  [2]=>
  string(3) "100"
}
array(3) {
  [0]=>
  string(9) "ぶどう"
  [1]=>
  string(3) "青"
  [2]=>
  string(3) "150"
}
array(3) {
  [0]=>
  string(9) "バナナ"
  [1]=>
  string(3) "黄"
  [2]=>
  string(3) "170"
}

終わりに

今回はテキストとCSVファイルをSplFileObjectクラスを使って読み込んでみました。
CSVファイルについてもsetFlagsメソッドに定数を設定することで、簡単に読み込むことができました。

SplFileObjectクラスには、他にもメソッドが用意されています。
公式のドキュメントのこちらに詳細な情報があるので確認してみてください。

コメント

タイトルとURLをコピーしました