ファイルに書く
テクノロジ系 / file-io (read / write / CSV / paths / upload概念)
ファイルに書く
Lesson 14 / Chapter 3
書き込みも 2 系統
| 関数 | どんな時 |
|---|---|
file_put_contents($path, $str) | 文字列を 1 発で書く / 追記する |
fopen + fwrite + fclose | 段階的に書きたい / 大きいデータを流す |
<?php // パターン A: 一発で書く $tmp = tempnam(sys_get_temp_dir(), 'dojo_'); file_put_contents($tmp, "Hello, File!\n"); // パターン B: fopen → fwrite → fclose $fp = fopen($tmp, 'w'); // 'w' = 上書きモード fwrite($fp, "line1\n"); fwrite($fp, "line2\n"); fclose($fp); unlink($tmp); // クリーンアップ
file-io トピックでは 大半が A で十分。B は慣れのため一度書く。
書き込み先は OS の一時ディレクトリへ
// ✅ OK: 一時ディレクトリ $tmp = tempnam(sys_get_temp_dir(), 'dojo_'); file_put_contents($tmp, "hi\n"); unlink($tmp); // ❌ NG: drill 内に書く (リポジトリが汚れる) file_put_contents(__DIR__ . '/output.txt', "hi\n");
sys_get_temp_dir()= OS の一時領域 (Mac/Linux なら/tmp)tempnam($dir, $prefix)= その中に 被らないファイル名 を作って返す (ゼロバイトの空ファイル付き)unlink($path)= ファイルを消す
→ 「採点が何度走っても、リポジトリの状態が変わらない」 のがゴール。
file_put_contents の典型形
<?php
$tmp = tempnam(sys_get_temp_dir(), 'dojo_');
// 上書き (デフォルト)
$bytes = file_put_contents($tmp, "Hello, File!\n");
echo "wrote {$bytes} bytes\n"; // wrote 13 bytes
// 読み返して確認
echo file_get_contents($tmp); // Hello, File!
unlink($tmp);- 戻り値は 書いたバイト数 (失敗時
false) - ファイルが既にあれば デフォルトで上書き
- 第 3 引数で挙動を変えられる (
FILE_APPEND/LOCK_EXなど)
追記モード — FILE_APPEND
<?php $tmp = tempnam(sys_get_temp_dir(), 'dojo_'); file_put_contents($tmp, "line1\n"); // 中身: "line1\n" file_put_contents($tmp, "line2\n", FILE_APPEND); // 中身: "line1\nline2\n" file_put_contents($tmp, "line3\n", FILE_APPEND); // 中身: "line1\nline2\nline3\n" echo file_get_contents($tmp); // line1 // line2 // line3 unlink($tmp);
FILE_APPENDを渡すと 末尾に追記 (元の中身を保つ)- 渡さないと 上書き (前の中身は消える)
→ ログ書き込みは FILE_APPEND | LOCK_EX のセットが定番。
fopen のモード一覧 (主要なもの)
| モード | 意味 | 既存ファイル |
|---|---|---|
'r' | 読み込み | そのまま |
'w' | 書き込み (上書き) | 中身が消える (truncate) |
'a' | 追記 | 末尾に追記 |
'r+' | 読み書き | そのまま |
'w+' | 読み書き (上書き) | 中身が消える |
'a+' | 読み書き (追記) | 末尾に追記 |
$fp = fopen($tmp, 'a'); // 追記モード fwrite($fp, "extra line\n"); fclose($fp);
「w は中身を消す」 が一番の落とし穴。設定ファイルを 'w' で開いて空にしてしまう事故が定番。
書いたら必ず読み返して検算
<?php $tmp = tempnam(sys_get_temp_dir(), 'dojo_'); // 書く file_put_contents($tmp, "Hello, File!\n"); // 読む (= 書いた内容を検算) echo file_get_contents($tmp); // 後始末 unlink($tmp);
- 採点では 「書いたものを読み返した結果」 を
expected.txtと比べる - 「書いた」と「保存できた」は 別物。
fclose/file_put_contentsの戻り値で確認する癖を
→ ドリル 01 はこの 「書く → 読む → 消す」のラウンドトリップ を体験する形。
このチャプターでできるようになること
✅ file_put_contents でファイルに 1 発で書ける
✅ fopen('w') + fwrite + fclose で書ける
✅ FILE_APPEND / fopen('a') で追記できる
✅ 'w' モードは 既存の中身を消す ことを認識している
✅ 書き込み先は sys_get_temp_dir() 配下にして unlink で消す癖がついた
→ ドリルへ
考えてみよう
既定では正解・不正解の判定はしません。自分のペースで「答えを見る」を。採点してほしい場合だけ「採点してほしい」を押してください。
ドリル 01 — 一時ファイルに書いて読み返す
問題
OS の一時ディレクトリに一時ファイルを作って "Hello, File!\n" を書き込み、その中身を読み返して標準出力に出してください。最後にその一時ファイルを unlink で消してください。
期待される出力:
Hello, File!
採点
php scripts/grade.php topics/file-io/ch03-write-file/drill/01-write-and-read/
ヒント
$tmp = tempnam(sys_get_temp_dir(), 'dojo_io_'); file_put_contents($tmp, "Hello, File!\n"); echo file_get_contents($tmp); unlink($tmp);
tempnam($dir, $prefix):$dir配下に被らないファイル名を作り、空ファイルを置いてパスを返すsys_get_temp_dir(): OS の一時ディレクトリパス (Mac/Linux なら/tmp)unlink($path): ファイルを削除する
なぜ一時ディレクトリに書くのか
- リポジトリ内 (
__DIR__配下) に書くと、採点を回すたびにリポジトリが汚れる sys_get_temp_dir()配下 +unlinkで消す = 「実行後は何も残らない」 が file-io トピックの作法
つまづいたら
Hello, File!の後に空行が 1 つ余計に出る
→ echo file_get_contents($tmp) . "\n"; のように追加で改行を足していないか
unlinkでWarningが出る
→ tempnam の戻り値 (フルパス) を渡しているか。null や空文字を渡していないか
ドリル 02 — FILE_APPEND で追記する
問題
一時ファイルに以下の順で書き込み、最終的な中身を読み返して出力してください。最後に unlink で削除します。
"line1\n"を書く (上書き)"line2\n"をFILE_APPENDで追記"line3\n"をFILE_APPENDで追記- ファイル全体を読み返して
echo unlinkで消す
期待される出力:
line1 line2 line3
採点
php scripts/grade.php topics/file-io/ch03-write-file/drill/02-append/
ヒント
$tmp = tempnam(sys_get_temp_dir(), 'dojo_io_'); file_put_contents($tmp, "line1\n"); file_put_contents($tmp, "line2\n", FILE_APPEND); file_put_contents($tmp, "line3\n", FILE_APPEND); echo file_get_contents($tmp); unlink($tmp);
FILE_APPENDを渡さないと 上書き されて前の中身が消える- ログ書き込みでは
FILE_APPEND | LOCK_EX(排他ロック付き追記) が定番
つまづいたら
- 出力が
line3だけになる
→ 2 回目以降の file_put_contents に FILE_APPEND を渡し忘れている (= 上書き)
line1line2line3のように改行が無い
→ 各文字列の末尾 "\n" を書き忘れている ("line1" ではなく "line1\n")