ファイルを読む
テクノロジ系 / 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) の形で書く