採点されません。匿名です。 間違えることは学習の一部です。読むだけでもかまいません。

ファイルを読む

テクノロジ系 / file-io (read / write / CSV / paths / upload概念)

ファイルを読む

Lesson 14 / Chapter 2

2 つの読み方を持っておく

関数どんな時
file_get_contents($path)小さい / 中身を文字列でまとめて欲しい とき
fopen + fgets + fclose大きい / 1 行ずつ処理したい とき
<?php
// パターン A: 一発で全部
$text = file_get_contents(__DIR__ . '/tests/data/hello.txt');

// パターン B: 1 行ずつ
$fp = fopen(__DIR__ . '/tests/data/hello.txt', 'r');
while (($line = fgets($fp)) !== false) {
    echo $line;   // $line には末尾の \n も含まれる
}
fclose($fp);

「設定読み込みは A」「ログ走査は B」 くらいの肌感で使い分けると良い。

file_get_contents の挙動

$path = __DIR__ . '/tests/data/hello.txt';
$text = file_get_contents($path);

var_dump($text);
// string(36) "こんにちは
// ファイル読み込みのテスト
// "
  • 成功すると ファイル全体を文字列で返す
  • 失敗すると false を返す (パス間違い・権限なしなど)
  • そのまま echo すれば中身がそのまま出る (末尾改行も含む)

「末尾に \n が含まれてる前提」 で出力を組み立てる。echo $text . "\n" のように追加で改行を足すと 空行が増える 罠あり。

fopen / fgets / fclose の作法

<?php
$fp = fopen(__DIR__ . '/tests/data/hello.txt', 'r');
if ($fp === false) {
    fwrite(STDERR, "open failed\n");
    exit(1);
}

while (($line = fgets($fp)) !== false) {
    // $line には末尾の改行 (\n / \r\n) が含まれる
    echo rtrim($line) . "\n";
}

fclose($fp);
ステップ役割
fopen($path, 'r')ファイルを 読み込みモード で開く
fgets($fp)1 行読む (末尾改行付き)。EOF で false
fclose($fp)ファイルを 閉じる (OS のリソースを返す)

fclose を忘れても PHP 終了時に解放されるが、「開いたら閉じる」 を癖にする。

なぜ fgets のループは while (($line = fgets($fp)) !== false) なのか

// ❌ よくある罠: feof で書くと最後の行を 1 個多く読む
while (!feof($fp)) {
    $line = fgets($fp);   // EOF 直前で fgets が false を返すが、ループは 1 回余分に回る
    echo $line;
}

// ✅ 安全: fgets の戻り値を直接判定
while (($line = fgets($fp)) !== false) {
    echo $line;
}
  • feof直前の読み取りで EOF を踏んだ後 に true になる → 「false が来てから初めて気づく」
  • 戻り値で判定すれば EOF を踏んだ瞬間に抜ける

→ 「fgets の戻り値で判定」を defalut 形として覚える。

パスは __DIR__ から組む

// ❌ NG: cwd (現在ディレクトリ) に依存する
$text = file_get_contents('tests/data/hello.txt');

// ✅ OK: スクリプト自身のディレクトリ基準
$text = file_get_contents(__DIR__ . '/tests/data/hello.txt');
  • 採点ランナー (php scripts/grade.php drill/01-read-all/) は リポジトリルートから起動 される
  • cwd 相対パスだと、起動位置がズレた瞬間 false (= パス間違い) で落ちる
  • __DIR__ を基準 にすれば、どこから呼ばれてもファイルを見つけられる

→ file-io トピックでは 「パスは必ず __DIR__ から組む」 が鉄則。

このチャプターでできるようになること

file_get_contents で 1 発でテキストを読める

fopen / fgets / fclose で 1 行ずつ読める

fgets ループは 戻り値判定 (!== false) で書ける

✅ パスは __DIR__ 基準 で組める

✅ 読み込んだ文字列の 末尾改行 を意識して echo できる

→ ドリルへ

考えてみよう

既定では正解・不正解の判定はしません。自分のペースで「答えを見る」を。採点してほしい場合だけ「採点してほしい」を押してください。

ドリル 01 — ファイル全体を読む

問題

__DIR__ . '/tests/data/hello.txt' の中身を そのまま標準出力に出す PHP を書いてください。

データファイル (tests/data/hello.txt):

こんにちは
ファイル読み込みのテスト

期待される出力:

こんにちは
ファイル読み込みのテスト

(末尾の改行はファイルに含まれている前提。echo で追加の "\n" を足さない)

採点

php scripts/grade.php topics/file-io/ch02-read-file/drill/01-read-all/

ヒント

$path = __DIR__ . '/tests/data/hello.txt';
echo file_get_contents($path);
  • file_get_contentsファイル全体を文字列で返す
  • 末尾の改行も含めて返すので、echo だけで OK
  • パスは __DIR__ 基準 で組む (cwd 依存にしない)

つまづいたら

  • Warning: file_get_contents(...): Failed to open stream

→ パスがズレている。__DIR__ . '/tests/data/hello.txt' の綴り / / の数を見直す

  • 末尾に空行が 1 つ余計に出る

echo file_get_contents($path) . "\n"; のように追加で改行を足していないか確認

ドリル 02 — 1 行ずつ読んで行番号を付ける

問題

__DIR__ . '/tests/data/poem.txt'1 行ずつ 読み、各行の先頭に "N: " (N は 1 始まりの行番号) を付けて出力する PHP を書いてください。

データファイル (tests/data/poem.txt):

春はあけぼの
やうやう白くなりゆく山ぎは
すこしあかりて
紫だちたる雲のほそくたなびきたる

期待される出力:

1: 春はあけぼの
2: やうやう白くなりゆく山ぎは
3: すこしあかりて
4: 紫だちたる雲のほそくたなびきたる

採点

php scripts/grade.php topics/file-io/ch02-read-file/drill/02-read-line-by-line/

ヒント

$fp = fopen(__DIR__ . '/tests/data/poem.txt', 'r');
$n = 1;
while (($line = fgets($fp)) !== false) {
    echo $n . ': ' . rtrim($line, "\r\n") . "\n";
    $n++;
}
fclose($fp);
  • fopen($path, 'r')読み込みモード で開く
  • fgets($fp)1 行返す (EOF で false)
  • fgets の戻り値には 末尾改行 が含まれるので rtrim($line, "\r\n") で剥がし、自分で "\n" を付け直す
  • 開いたら必ず fclose

つまづいたら

  • 最後に空行が 1 つ余分に出る

fgets の戻り値末尾の改行を剥がさず、さらに "\n" を付け足している

  • 1 行ズレる / 行が 1 個多い

while (!feof($fp)) で書くと末尾で 1 回余分に回ることがある。while (($line = fgets($fp)) !== false) の形で書く