PHP の fread・fgets・fgetcsv — 読み込み関数の使い分け

PHP にはファイルからデータを読み込む関数が複数用意されている。fread はバイト単位で読み取り、fgets は 1 行ずつ読み取り、fgetcsv は CSV の 1 行を配列として返す。それぞれ得意な場面が異なるため、目的に応じた選択が重要になる。

fread — バイト単位の読み込み

fread はファイルハンドルから指定したバイト数だけデータを読み込む関数だ。テキストでもバイナリでも扱える汎用的な関数で、読み込むサイズを自分で制御できる。

$fp = fopen('data.txt', 'r');
$content = fread($fp, 1024); // 最大1024バイト読み込み
fclose($fp);

ファイル全体を読み込みたい場合は、filesize と組み合わせる。

$fp = fopen('data.txt', 'r');
$content = fread($fp, filesize('data.txt'));
echo $content;
fclose($fp);

ファイルの終端に達すると、指定バイト数に満たなくても読み込みが終わる。ファイルポインタが末尾かどうかは feof で確認できる。

$fp = fopen('data.bin', 'rb');

while (!feof($fp)) {
    $chunk = fread($fp, 8192);
    // チャンク単位で処理
}

fclose($fp);

この方法なら巨大なファイルでもメモリを大量に消費せず、8KB ずつ処理を進められる。

fgets — 1 行ずつの読み込み

fgets は改行文字(\n)またはファイル終端まで読み取り、1 行分の文字列を返す。テキストファイルを行単位で処理するときの定番だ。

$fp = fopen('log.txt', 'r');

while (($line = fgets($fp)) !== false) {
    echo trim($line) . "\n";
}

fclose($fp);

fgets が返す文字列には末尾に改行が含まれるため、trimrtrim で除去するのが一般的だ。第 2 引数で最大読み込みバイト数を制限することもできる。

$line = fgets($fp, 256); // 最大255バイトまで読み込み

非常に長い行があるファイルでも、バイト数を制限しておけばメモリの使いすぎを防げる。

fread

バイト数を指定して読み込む。バイナリファイルやチャンク処理に向いている。

fgets

改行までを 1 行として読み込む。テキストファイルの行単位処理に最適。

fgetcsv — CSV の 1 行を配列で取得

fgetcsv は CSV 形式のファイルを 1 行読み込み、各フィールドを配列として返す。自分でカンマ分割する手間が省けるうえ、ダブルクォートで囲まれたフィールド内のカンマも正しく処理してくれる。

$fp = fopen('users.csv', 'r');

while (($row = fgetcsv($fp)) !== false) {
    echo "名前: {$row[0]}, 年齢: {$row[1]}\n";
}

fclose($fp);

デフォルトの区切り文字はカンマだが、第 3 引数で変更できる。タブ区切り(TSV)の場合は次のようにする。

$row = fgetcsv($fp, 0, "\t");

第 2 引数の 0 は行の最大長を自動判定させる指定だ。

ヘッダー行の処理

CSV ファイルの先頭にヘッダー行がある場合、最初の 1 行を読み飛ばすか、カラム名として活用するのが一般的な手法だ。

$fp = fopen('products.csv', 'r');
$headers = fgetcsv($fp); // ヘッダー行を取得

while (($row = fgetcsv($fp)) !== false) {
    $product = array_combine($headers, $row);
    echo "{$product['name']}: {$product['price']}円\n";
}

fclose($fp);

array_combine でヘッダーをキーにした連想配列を作ると、$row[0] のようなインデックスアクセスより可読性が大幅に向上する。

文字エンコーディングの注意

日本語を含む CSV を扱うとき、エンコーディングの問題にぶつかることがある。Excel で作成された CSV は Shift_JIS(CP932)であることが多いが、PHP 内部では UTF-8 で処理するのが標準的だ。

$fp = fopen('data_sjis.csv', 'r');

while (($row = fgetcsv($fp)) !== false) {
    $row = array_map(function ($field) {
        return mb_convert_encoding($field, 'UTF-8', 'SJIS-win');
    }, $row);
    // UTF-8 に変換して処理
    echo implode(' | ', $row) . "\n";
}

fclose($fp);

エンコーディングの自動判定は不安定なので、ファイルの出所がわかっている場合は SJIS-win のように明示的に指定するのが安全だ。

Shift_JIS の Windows 拡張。丸囲み数字やローマ数字など、CP932 固有の文字も正しく変換できる。

3 つの関数の使い分け

どの読み込み関数を選ぶかは、ファイルの形式と処理の粒度で決まる。バイナリデータやチャンク処理なら fread、テキストの行処理なら fgets、CSV なら fgetcsv というのが基本的な判断基準になる。いずれも fopen / fclose と組み合わせて使うため、ファイルハンドルの管理を忘れないようにしたい。