Pythonの基本文法を覚えて、簡単なプログラムが動くようになってきた。それなのに、実行が終わると結果がきれいさっぱり消えてしまう。
画面に表示された数字は、ウィンドウを閉じた瞬間になくなります。せっかく集計した結果を明日も使いたいとき、どうすればいいのでしょうか。
答えはファイルです。プログラムの外にデータを残す方法として、いちばん手軽で確実なのがファイルへの読み書きになります。
この記事では、Pythonでテキストファイルを扱う基本を順番に追いかけます。初心者がほぼ必ずぶつかる文字化けと上書き事故も、あわせて片づけていきましょう。
ファイルを開くとは、どういうことなのか¶
日常会話で言うファイルを開くは、アイコンをダブルクリックして中身を見ることを指します。プログラムの世界では、もう少し具体的な意味を持っています。
Pythonがファイルを開くとき、裏側ではOSに対して、このファイルを使わせてくださいという申請が飛んでいます。OSはそれを受けて、読み書きのための通り道を用意します。
この通り道には数に限りがあります。だから使い終わったら、閉じますと伝えて返す必要があるわけです。
開いて、読み書きして、閉じる。この3拍子がファイル操作の全体像で、Pythonのコードもきれいにこの順番どおりに並びます。
open関数は、名前とモードとencodingで決まる¶
ファイルを開く役目を持つのがopenという組み込み関数です。引数はたくさんありますが、日常的に使うのは最初の3つだけと考えて構いません。
まずは、いちばん短い形を見てみましょう。
f = open("memo.txt", "r", encoding="utf-8")
print(f.read())
f.close()
1つ目がファイル名、2つ目がモード、3つ目が文字の解釈方法です。最後のcloseで通り道を返しています。
このコードは動きますが、実務で書くことはまずありません。理由はあとで説明しますが、先にモードとencodingを押さえておきましょう。
モードは、読むのか書くのかを伝える合図¶
モードは短いアルファベットで指定します。似た文字が並ぶので、最初は混乱しやすいところです。
よく使うものを表にまとめました。
| モード | 意味 | ファイルが無いとき | 既存の中身 |
|---|---|---|---|
r |
読み込み専用(既定値) | エラーになる | そのまま |
w |
書き込み | 新しく作る | すべて消える |
a |
追記 | 新しく作る | 残る(末尾に追加) |
x |
新規作成のみ | 新しく作る | エラーになる |
rb / wb |
バイナリで読み書き | rはエラー、wは作成 | wは消える |
覚え方はシンプルで、readのr、writeのw、appendのaです。モードを省略するとrになるので、読むだけなら指定しなくても動きます。
この表でいちばん気をつけたいのは、wが既存の中身を消してしまうことです。追記のつもりでwを渡してしまい、前日までのログが空になった。私は10年ほどエンジニアとして開発に関わってきましたが、この事故は新人時代に自分でやりましたし、他人のコードでも何度か見ています。
encodingを省略すると、環境で結果が変わる¶
encodingは、ファイルの中のバイト列をどの文字として読むかの指定です。日本語を扱うなら、ここを省略してはいけません。
なぜなら、省略したときの既定値は動かす環境によって変わるからです。Linuxやmacではだいたいutf-8になり、日本語版Windowsではcp932になることがあります。
同じコードが自分のMacでは動くのに、同僚のWindowsでは文字化けする。原因の8割はencodingの省略だと思ってよいくらい、定番のトラブルです。
対策は簡単で、テキストを開くときは必ずencoding="utf-8"と書くだけ。たったこれだけで、環境差の悩みがほとんど消えます。
with文を使えば、閉じ忘れが起きない¶
さきほどのコードにはcloseがありました。実は、あれを手で書く方法には弱点があります。
readの途中でエラーが起きると、closeまで到達しません。ファイルが開きっぱなしになり、Windowsでは他のプログラムから触れなくなることもあります。
そこで登場するのがwith文です。ブロックを抜けるときに、エラーが起きたかどうかに関係なく自動で閉じてくれます。
with open("memo.txt", encoding="utf-8") as f:
print(f.read())
# ここに来た時点で、fはもう閉じている
closeが消えて、行数も減りました。Pythonでファイルを開くときは、常にwith文を使うと覚えてしまって構いません。
この自動で後始末をする仕組みは、コンテキストマネージャーと呼ばれています。仕組みそのものが気になる方は、こちらもどうぞ。【関連記事】pythonのコンテキストマネージャーって何?詳しく解説します!
読み込みには、3つのやり方がある¶
ファイルを読む書き方はひとつではありません。データの量と、やりたいことで選び分けます。
3つの方法を、使いどころとあわせて並べてみます。
| 書き方 | 返ってくるもの | 向いている場面 |
|---|---|---|
f.read() |
全体が1つの文字列 | 小さいファイルをまとめて扱う |
f.readlines() |
1行ずつのリスト | 行数を数える、あとで並べ替える |
for line in f |
1行ずつ順番に | 大きいファイル、ログの走査 |
readとreadlinesは、ファイル全体をメモリに載せます。数KBのメモなら問題ありませんが、数GBのログでやると一気にメモリが埋まります。
一方でforを使う書き方は、1行ずつ読んでは捨てるので、ファイルがどれだけ大きくてもメモリは増えません。迷ったらforを選んでおくと安全です。
実際に動かしてみましょう。
with open("access.log", encoding="utf-8") as f:
for i, line in enumerate(f, start=1):
line = line.rstrip("\n")
if "ERROR" in line:
print(f"{i}行目: {line}")
rstripで末尾の改行を落としているのがポイントです。ファイルから読んだ行には改行文字がくっついたままなので、そのまま表示すると余計な空行が出ます。
書き込みは、上書きと追記を意識して選ぶ¶
書くときも同じくwith文を使います。違うのはモードだけです。
まずは新しくファイルを作って、中身を入れてみます。
lines = ["りんご", "みかん", "ぶどう"]
with open("fruits.txt", "w", encoding="utf-8") as f:
for name in lines:
f.write(name + "\n")
with open("fruits.txt", "a", encoding="utf-8") as f:
f.write("もも\n")
前半がwで新規作成、後半がaで追記です。writeはprintと違って改行を自動で付けないので、自分で\nを足す必要があります。
ここを忘れると、りんごみかんぶどうと1行にくっついて出てきます。よくあるつまずきなので、書き込みのときは改行を意識してください。
改行そのものの扱いが不安な方は、こちらの記事もあわせてどうぞ。【関連記事】Pythonの改行を初心者向けに解説!print・文字列・ファイル出力で迷わない基本
文字化けが起きたときに、どこを疑うか¶
encodingを指定していても、文字化けやエラーに出会うことはあります。多くは、読む側と書いた側の食い違いが原因です。
Excelから出したファイルがcp932で保存されているのに、utf-8で読もうとした。この場合はUnicodeDecodeErrorという例外が出ます。
エラーになるだけまだ親切で、いちばん厄介なのは化けた文字がそのまま通ってしまうケースです。データベースに入るまで誰も気づかない、という事態になりかねません。
対処の順番を整理しておきます。
| 症状 | 疑うところ | 対応 |
|---|---|---|
| UnicodeDecodeErrorが出る | 読むときのencodingが違う | cp932 や shift_jis を試す |
| 先頭に見慣れない文字が付く | BOM付きutf-8 | encoding="utf-8-sig" にする |
| Excelで開くと化ける | 書くときのencoding | 書き出しを utf-8-sig にする |
utf-8-sigは、日本の実務でかなり出番があります。ExcelはBOMが無いutf-8のcsvを素直に読んでくれないことがあるためです。
このあたりはcsvを扱うときに集中して出てくる話題なので、表データを読み書きする予定がある方はこちらもどうぞ。【関連記事】Pythonのcsvモジュールとは?表データの読み書きと文字化けの防ぎ方を初心者向けに解説
テキストで開くか、バイナリで開くか¶
ここまではすべてテキストモードの話でした。モードにbを付けると、バイナリモードに切り替わります。
違いは、Pythonが中身を文字として解釈するかどうかです。テキストモードはバイト列を文字列に変換し、バイナリモードは変換せずbytes型のまま渡します。
画像やPDF、zipのようなファイルは、文字として解釈しようとすると壊れます。だから画像をコピーするようなコードでは、必ずbを付けます。
with open("logo.png", "rb") as src:
data = src.read()
with open("copy.png", "wb") as dst:
dst.write(data)
バイナリモードではencodingを指定できません。文字として解釈しない以上、指定する意味がないからです。
判断はシンプルで、人が読める中身ならテキスト、それ以外はバイナリと考えておけば大きく外しません。JSONのように構造を持ったテキストは、専用のモジュールに任せるのが定石です。【関連記事】PythonでJSONデータの扱いをマスター!API連携に必須のjsonモジュールの使い方
Python 3.15から、encodingの既定が変わる¶
ここまでencodingを必ず書きましょうと繰り返してきました。実は、この事情は今後ゆるやかに変わっていきます。
PEP 686という提案が承認され、Python 3.15からはUTF-8モードが既定で有効になります。つまり、encodingを省略したときの動きがどの環境でもutf-8に揃うわけです。
Python 3.15は2026年10月1日にリリース予定です。長らく日本のPython開発者を悩ませてきた環境差が、ようやく標準の側で解消されることになります。
とはいえ、しばらくは古いバージョンも現役です。明示的にencodingを書く習慣は、これからも持っておいて損はありません。
なお、encodingを省略している箇所を洗い出したいときは、実行時にオプションを付ける方法があります。
python -X warn_default_encoding your_script.py
こうするとEncodingWarningという警告が出て、指定を省いた行が見つかります。3.15への移行前に一度かけてみると、直すべき場所が一目でわかります。3.15の変更点全体を知りたい方は、こちらもどうぞ。【関連記事】Python 3.15の新機能を先取り解説!lazy importやfrozendictで何が変わるのか
まとめ¶
ファイルの読み書きは、Pythonを実務で使い始めるときの最初の関門です。押さえるところは多くありません。
with文で開いて、モードを正しく選び、encodingを明示する。この3点さえ守れば、初心者がぶつかる事故のほとんどは避けられます。
私自身、10年たった今でもwとaの取り違えには神経を使っています。上書きは一瞬で終わり、取り返しがつかないからです。
まずは手元にmemo.txtを作って、書いて、読んで、追記してみてください。手を動かすと、開いて閉じるという流れが体になじみます。
ここまでお読みいただきありがとうございました。