file_get_contentsとfopen、どちらで読むか

file_get_contents とはファイルの中身を一括で文字列として読む関数、fopen とはストリームを開いて少しずつ読むための関数です。サイズで使い分けます。

一括で読むfile_get_contents

ファイルの中身をまるごと文字列で欲しいだけなら file_get_contents が一番手軽です。開いて読んで閉じる、を一行で済ませてくれます。設定ファイルや小さめのテキストを読むなら、これで十分ですね。

$content = file_get_contents('/path/to/config.json');
$config = json_decode($content, true);

// 一部だけ読む(オフセットと長さも指定できる)
$head = file_get_contents('/path/to/big.log', false, null, 0, 1024);

手軽な反面、ファイル全体をメモリに載せるのが弱点です。数百MBのファイルを読むと、そのぶんメモリを食います。ここが判断の分かれ目になります。

ストリームで少しずつ読むfopen

大きなファイルや、行ごとに処理したいときは fopen でストリームを開き、少しずつ読むのが定石です。fgets で1行ずつ、あるいは fread で決まったサイズずつ読めば、ファイルがどれだけ大きくてもメモリ使用量はほぼ一定に保てます。

$fp = fopen('/path/to/huge.log', 'r');
if ($fp === false) {
    throw new RuntimeException('開けませんでした');
}

while (($line = fgets($fp)) !== false) {
    // 1行ずつ処理。メモリは一定
    processLine($line);
}

fclose($fp); // 閉じ忘れに注意

fgets の戻り値が空行のときは空文字を返し、終端で false を返します。while ($line = fgets(...)) だと空行で止まってしまうので、!== false で厳密に比較するのが安全ですね。

使い分けの目安とメモリの考え方

私の中の目安は単純で、「全部メモリに載っても平気なサイズか」で決めています。数十KB〜数MB程度で、中身をまとめて扱いたいなら file_get_contents。行単位の処理や、サイズが読めない・大きいなら fopen のストリーム、という感じです。行ごとに配列で欲しいだけなら file() という手もありますが、これも全行をメモリに載せる点は同じです。

// 行配列が欲しいが、改行は要らないとき
$lines = file('/path/to/small.txt', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);

// メモリ効率を保ちつつ整形したいならジェネレータにする
function readLines(string $path): Generator {
    $fp = fopen($path, 'r');
    try {
        while (($line = fgets($fp)) !== false) {
            yield rtrim($line, "\r\n");
        }
    } finally {
        fclose($fp);
    }
}

まとめ

file_get_contents は手軽さ、fopen のストリームはメモリ効率、という住み分けだと思います。小さいファイルは一括、大きいファイルや行処理はストリーム、と判断すれば大抵うまくいきます。ストリームでは fclose の閉じ忘れと fgets の false 判定、この2点だけ丁寧にやっておけば安心して大きなファイルも扱えると思います。

よくある質問

Q. どちらで読むか迷ったら?
A. 全部メモリに載っても平気なサイズなら file_get_contents、行単位の処理や大きなファイルなら fopen のストリーム、というのが私の目安です。

Q. fgets のループで空行に注意することは?
A. fgets は空行で空文字、終端で false を返します。while ($line = fgets(...)) だと空行で止まるので、!== false で厳密に比較します。

Q. file() との違いは?
A. file() は全行を配列で返しますが、これも全行をメモリに載せます。メモリ効率を保ちたいなら、ジェネレータで1行ずつ yield する手があります。

類似投稿

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です