順番に読み進めながら学べます

ファイル入出力 — with open() で安全に読み書きする

Python のファイル入出力を基礎から解説します。with open() による自動クローズの仕組み、read / readlines / readline の使い分け、w / a モードの違い、入力ファイルを読んで結果を別ファイルに保存するパターンまで、実用寄りの題材で丁寧に学べます。

プログラムが扱うデータの多くは、ハードディスク上の ファイル として保存されています。タスクリストを読み込んで一覧表示する、完了したタスクをログに書き出す、設定値をファイルから読む — どれも実務で頻繁に発生する操作です。

Python では with open() を使えば、これらをひとまとまりの安全な構文で記述できます。この記事では with open() の基本形から、読み込み 3 種 (read / readlines / readline)、書き込み (w) と追記 (a)、そして フォルダ階層を持つパス の扱いまで、実際にファイルを動かしながら身につけます。

with open() でファイルを安全に開閉する

「ファイルを開く」というのは OS にリソースを掴ませる操作で、開いたら 必ず閉じる 必要があります。閉じ忘れるとファイルディスクリプタが枯渇したり、書き込み内容がバッファに残ったままディスクに反映されないといった問題が起きます。

「開く」と OS がリソースを抱える — 必ず「閉じる」のが約束
open()OS が FDを確保読み書きclose()で解放open()OS が FDを確保読み書き閉じ忘れ→ FD 枯渇+ バッファ未反映正常
open() は OS に ファイルディスクリプタ (FD) を確保させる操作。close() を呼ぶまで FD は掴まれたまま、書き込みもバッファに残ったまま。閉じ忘れは深刻なリソースリークになる。

ファイルディスクリプタ (FD) とは

FD (file descriptor) は OS が「いま開いているファイル」 1 つひとつに割り振る 整数の ID です。open() のたびに新しい FD が払い出され、close() で OS に返却されます。プロセスが同時に持てる FD の数には OS が決めた上限(Linux は既定で約 1,024 個)があり、これを使い切ると新しい open()OSError: Too many open files で失敗するようになります。閉じ忘れの怖さはこの「数が決まっている資源を握ったままになる」点にあります。

with open(path, mode) as f: の形で書くと、ブロックを抜けた瞬間に 自動でファイルが閉じられる ため、こうしたミスがそもそも起こせません。as ff は開いたファイルオブジェクトを受ける変数で、with ブロックの中ではこの f 経由で読み書きします。

パスは スラッシュ / でフォルダを区切ります。たとえば open("data/tasks.txt", "r") と書けば、カレントディレクトリの data フォルダの中にある tasks.txt を開けます。Windows の \ ではなく / を使うのが Python の慣習です(内部で OS ごとに変換されます)。

with open() の流れ
with open(path, mode)as f で受け取るf.read() やf.write() を呼ぶ自動でcloseブロック終了
with のブロックに入った瞬間にファイルが開かれ as f に入る。ブロックを抜けるときは正常終了でも例外でも、自動で close() が走る。

読み込み — read / readlines / readline

ファイルを読み込むには open(path, "r")r(read = 読み込み)モードを使います。返ってきたファイルオブジェクトには 3 種類の読み込みメソッド があり、用途に応じて使い分けます。

- f.read() — ファイル全体を 1 つの文字列 として一気に読み込む

- f.readlines() — ファイル全体を 行ごとの文字列リスト として読み込む

- f.readline()1 行だけ 読み込む。繰り返し呼ぶと次の行が返り、末尾に達すると空文字列 "" が返る

3 つの読み込みメソッドが返すもの
f.read()f.readlines()f.readline()全文の str1 個行ごとのlist1 行 str(次は次行)
read は文字列、readlines はリスト、readline は 1 行ずつの文字列。扱うデータの大きさで使い分ける。
# 全文を 1 つの文字列で取得
with open("data/tasks.txt", "r") as f:
    content = f.read()
print(type(content))     # <class 'str'>

# 行ごとのリストで取得
with open("data/tasks.txt", "r") as f:
    lines = f.readlines()
print(type(lines))       # <class 'list'>

# 1 行ずつ取得(次の呼び出しで次行)
with open("data/tasks.txt", "r") as f:
    first  = f.readline()   # "資料を作成する\n"
    second = f.readline()   # "メールを返信する\n"

ここから先はブラウザ用の仮想ファイルシステム (VFS) で動かします

右のコンソールはブラウザ内の 仮想的なファイルシステム で、data/tasks.txt などの教材ファイルがあらかじめ用意されています。ローカル PC で同じコードを動かす場合 は、実行する Python ファイル(例: main.py)と 同じ階層に data/ フォルダを作り、その中に tasks.txt を配置 してから実行してください。パスの書き方("data/tasks.txt" のように / で区切る)は本物の Python と完全に同じです。

右のコンソール上部の 📂 ファイル ボタンを押すと、教材があらかじめ用意した data/tasks.txt(タスク 4 件)と data/log.txt(履歴 3 行)の 2 つのファイルが見られます。

with open("data/tasks.txt", "r") as f: でファイルを読み込みモードで開いてください。

f.read() で全文を取得して content という変数に入れてください。

print(content) で表示してください。

(正しく実行できれば解説が表示されます)

Python エディタ

コードを実行してください

1 行 = 1 件のレコード という形のファイル(CSV のヘッダ抜き、改行区切りのリスト、ログなど)を扱うときは、f.readlines() が便利です。返り値は 行ごとに分割された文字列のリスト で、各要素は末尾に改行 \n が付いた状態です。

そのまま print() すると改行が二重になって読みづらいため、各行に .rstrip("\n") を当てて末尾の改行を落とすのが定石です。

今度は data/log.txt(実行履歴 3 行)を readlines() でリストとして読み込み、行番号を付けて表示します。タスク管理ツールでログ画面を作る、運用レポートを整形する、といったときの典型パターンです。

with open("data/log.txt", "r") as f: で開き、f.readlines() の戻り値を lines に入れてください。

enumerate(lines, start=1)1 から 番号を振りながら for ループしてください。

③ 各行は末尾の改行を .rstrip() で落とし、f"{番号}: {本文}" の形で print してください。

Python エディタ

コードを実行してください

大きなファイルは 1 行ずつ — readline とセイウチ演算子

f.read()f.readlines()ファイル全体をメモリに展開 します。テキストの設定ファイル程度なら問題ありませんが、サーバーログのように 数 GB に達するファイルでは、メモリが足りずプログラムがクラッシュすることがあります。

f.readline() を使うと 1 行だけ をメモリに読み込めるため、メモリ使用量は常に 1 行分で済みます。readline() はファイル末尾に達すると 空文字列 "" を返すので、これを使ってループの終端を判定します。

readline() は 1 行ずつメモリに乗せる
ファイル(数 GB)readline()→ 1 行目メモリ:1 行目(続き)readline()→ 2 行目メモリ:2 行目末尾到達readline()→ ""ループ終了
1 行読む → 処理 → 次の行で上書き を繰り返す。ファイル全体をメモリに乗せないので、何 GB でもメモリは「最も長い 1 行」分で済む。末尾に達したら "" を返してループを抜ける合図になる。
# 古典的な書き方: 空文字 "" を見たら break
with open("data/log.txt", "r") as f:
    while True:
        line = f.readline()
        if not line:           # 空文字 = ファイル末尾
            break
        print(line.rstrip())

セイウチ演算子 := で同じことが 1 行少なく書ける

Python 3.8 で導入された セイウチ演算子 (walrus operator) := を使うと、while の条件式の中で 代入と判定を同時に 行えます。while line := f.readline(): と書けば、「readline() の結果を line に入れて、それが空文字でない間ループを続ける」 というコードを 1 行で表現できます。

今度は data/tasks.txtreadline() + セイウチ演算子 の形で 1 行ずつ表示してみましょう。実運用では巨大なログ処理に使うパターンですが、ここでは挙動を確認するためにタスクファイルで試します。

with open("data/tasks.txt", "r") as f: で開いてください。

while line := f.readline(): の形で line に 1 行ずつ代入しながらループしてください。

③ 末尾の改行を .rstrip() で落として print(line.rstrip()) で表示してください。

Python エディタ

コードを実行してください

書き込み — w モードと a モード

ファイルへの書き込みは、open() の第 2 引数を 書き込み系のモード に切り替えるだけです。実用上覚えておきたいのは 2 つです。

- "w"(write)— ファイルを 新規作成 or 全上書き。既存内容は 消える

- "a"(append)— ファイルの 末尾に追記。既存内容は 残る

書き込みメソッドも 2 つあります。f.write(s) は文字列 s をそのまま書き、f.writelines(lst) は文字列のリストをまとめて書き込みます。書き込み先のパスにフォルダを含めれば、その中にファイルが作られます(フォルダがあらかじめ存在している前提です)。

w と a の違い
既存:ABCopen("w")f.write("XYZ")結果:XYZ既存:ABCopen("a")f.write("XYZ")結果:ABCXYZ上書き追記
w は開いた瞬間に既存内容が消える上書きモード。a は既存内容の末尾から追記する。意図と逆を選ぶと 大事なログが吹き飛ぶ ので注意。
# w モード: 文字列を 1 件ずつ書く
with open("data/done.txt", "w") as f:
    f.write("資料を作成する\n")
    f.write("メールを返信する\n")

# w モード: リストをまとめて書く(writelines)
with open("data/done.txt", "w") as f:
    f.writelines(["資料を作成する\n", "メールを返信する\n", "会議に参加する\n"])

# a モード: 末尾に追記
with open("data/done.txt", "a") as f:
    f.write("買い物をする\n")

改行 \n は自動で入らない

print() と違い、f.write()f.writelines()改行を勝手に入れてくれませんf.write("Hello") を 2 回呼んでもファイルには HelloHello と並ぶだけです。意図した位置で改行したいときは 明示的に \n を入れる のが鉄則です。

data/ フォルダの中done.txt(完了タスクの記録)を新規作成して書き込み、その内容を読み返して表示します。data/ フォルダはすでに存在しているので、そのままパスに含めて open できます。

with open("data/done.txt", "w") as f: で開いてください。

f.write("完了タスク:\n") のあと、f.write("資料を作成する\n") f.write("メールを返信する\n") の順で 3 回書き込んでください。

③ 別の with open("data/done.txt", "r") as f: で開き直し、print(f.read()) で全文を表示してください。

④ 実行後、ヘッダーの 📂 ファイル ボタンを押すとパネルに data/done.txt が増えていることが目で確認できます。

Python エディタ

コードを実行してください

前の演習で作った data/done.txt のような状態を再現して、末尾に項目を追加 します。wa の挙動の違いをこの 1 本のコードで体験してみましょう。

① 最初に with open("data/done.txt", "w") as f: で開いて、f.write("完了タスク:\n資料を作成する\n") で初期内容をセットしてください(毎回同じ状態から始めるための初期化)。

② 次に with open("data/done.txt", "a") as f: で開いて、f.write("メールを返信する\n") を追記してください。

③ 最後に with open("data/done.txt", "r") as f: で開き直し、print(f.read()) で内容を確認してください。

Python エディタ

コードを実行してください

発展 — ファイルを分析して結果を別ファイルに保存する

実務でもっとも頻繁に書く処理の 1 つが、入力ファイルを読み込んで何らかの分析を行い、結果を別のファイルに書き出す という流れです。open() を 2 回使い、片方は "r"、もう片方は "w" にするだけで実現できます。入力 → 加工 → 出力 という 3 段階を、それぞれ別々の with ブロックに分けて書くのが読みやすくする定石です。

# data/log.txt を分析し、結果を data/log_summary.txt に保存する

# ① 入力: ログを読み込んで行リストにする
with open("data/log.txt", "r") as src:
    lines = src.readlines()

# ② 加工: 件数と最初/最後のエントリを集計
total   = len(lines)
first   = lines[0].rstrip()
last    = lines[-1].rstrip()
summary = f"件数: {total}\n最初: {first}\n最後: {last}\n"

# ③ 出力: 別ファイルに書き出す
with open("data/log_summary.txt", "w") as dst:
    dst.write(summary)

print("分析結果を data/log_summary.txt に保存しました")

data/products.txt に「商品名,値段」の形式で 4 行のデータが入っています。これを読み込んで 合計金額を計算 し、結果を data/total.txt という別ファイルに書き出します。

with open("data/products.txt", "r") as f: で開き、f.readlines() で行リストを取得してください。

② 各行を .rstrip() で末尾改行を落とし、.split(",")商品名と値段 に分解してください。値段は文字列なので int() で数値に変換 してから合計に加算してください。

with open("data/total.txt", "w") as f: で開き、f.write(f"合計: {total} 円\n") で結果を書き込んでください。

④ 書き込み内容を確認するため、別の with open("data/total.txt", "r") as f:print(f.read()) してください。

Python エディタ

コードを実行してください

文字エンコーディングは encoding="utf-8" が定石

実機の Python では open(path, "r", encoding="utf-8") のように エンコーディングを明示 します。Shift-JIS で保存されたファイルを UTF-8 で読み込もうとすると UnicodeDecodeError で止まり、その逆も同じく文字化けします。

世界標準は UTF-8 で、新規に保存するファイルは UTF-8 で書くのが原則です。ブラウザ実行環境では裏側で UTF-8 に固定されているので encoding= の指定がなくても動きますが、ローカルでスクリプトを書くときは付けるようにしましょう。

QUIZ

理解度チェック

まずは1問ずつ答えてみましょう。

Q1with open("data/notes.txt", "r") as f: の第 2 引数 "r" は何を表しますか?

Q2f.readline() を繰り返し呼んで、ファイルの末尾に達したときに返ってくる値はどれですか?

Q3既に内容のある data/done.txtwith open("data/done.txt", "w") as f: で開いたとき、既存の内容はどうなりますか?