Pythonでファイルを開くときのモード指定は、動作の差が小さいようでいて実は大きな違いがあります。
本記事では‘r’ ‘w’ ‘a’ と ‘+’の有無で何が変わるかを初学者向けに整理し、サンプルコードと実行結果で直感的に理解できるように解説します。
迷ったときの選び方やエラー回避のコツもまとめます。
Pythonファイルモード入門(‘r’ ‘w’ ‘a’ ‘+’)
ファイルモードとは? 使い分けの基準
ファイルモードは「何ができるか(読み・書き)」と「既存の内容をどう扱うか(作成・上書き・追記)」を同時に決める設定です。
Pythonのopen(path, mode)
に渡すmode
文字列で挙動が決まります。
基本の3種である'r'
・'w'
・'a'
に'+'
を付けると「読みと書きの両方」を許可します。
さらに't'
(テキスト)・'b'
(バイナリ)を併記して、データの扱い方を選べます。
覚え方の軸は次の2点です。
1つは「読み書きの可否」、もう1つは「既存ファイルの扱い(消す・保つ・末尾へ追加)」。
また、開いた直後のカーソル位置(ファイルポインタ)
も重要です。
下表は概要です。
詳細は後述の各節で確認してください。
モード | 読み込み | 書き込み | 新規作成 | 既存内容 | 初期カーソル位置 | 書き込み位置 |
---|---|---|---|---|---|---|
‘r’ | 可 | 不可 | 不可 | 保つ | 先頭 | ー |
‘w’ | 不可 | 可 | 可 | 消す | 先頭 | 現在位置(先頭)から上書き |
‘a’ | 不可 | 可 | 可 | 末尾へ追記 | 末尾 | 常に末尾 |
‘r+’ | 可 | 可 | 不可 | 保つ | 先頭 | 現在位置から上書き |
‘w+’ | 可 | 可 | 可 | 消す | 先頭 | 現在位置(先頭)から上書き |
‘a+’ | 可 | 可 | 可 | 末尾へ追記 | 末尾 | 常に末尾 |
‘w’と’w+’は開いた瞬間に既存の内容を消去(トランケート)します。
これが最大の事故ポイントです。
反対に‘a’と’a+’は既存内容を保ちながら書き込みを常に末尾へ行います。
テキスト(‘t’)とバイナリ(‘b’)の違い
't'
はテキストモードで、文字列(str
)として読み書きします。
改行コードの扱いはOSに応じて適宜変換され、encoding='utf-8'
などの文字エンコーディング指定が可能です。
'b'
はバイナリモードで、bytes
をそのまま読み書きします。
画像や圧縮ファイルなどはバイナリで扱います。
ポイント: テキストはstr
、バイナリはbytes
。
型が違うため混在させず、必要なら.encode()
/.decode()
で明示的に変換します。
サンプルコード(テキストとバイナリの違い)
# テキスト('t')モードとバイナリ('b')モードの違いを確認
# ファイルを作成して文字列を書き込み、その後バイナリで読み出します。
# テキストモードで書く(デフォルトは't')
with open("tb_sample.txt", "w", encoding="utf-8") as f:
f.write("Hello\n") # strとして書く
# バイナリモードで読むと、bytesで取得される
with open("tb_sample.txt", "rb") as f:
data = f.read()
print(type(data), data) # <class 'bytes'> と b'Hello\n' のように表示される
<class 'bytes'> b'Hello\n'
‘r’ ‘w’ ‘a’の意味と注意点
‘r'(読み込み専用) 存在しないとエラー
‘r’は読み込み専用。
ファイルが存在しないとFileNotFoundErrorになります。
安全に扱うには、例外処理で捕捉するか、存在確認を行います。
サンプルコード(‘r’でのエラーと通常読み込み)
# 1) 存在しないファイルを'r'で開く -> 例外を確認
try:
with open("r_demo.txt", "r", encoding="utf-8") as f:
print(f.read())
except FileNotFoundError as e:
print("存在しない場合のエラー:", e)
# 2) ファイルを作ってから'r'で読む -> 正常に読める
with open("r_demo.txt", "w", encoding="utf-8") as f:
f.write("一行目\n二行目")
with open("r_demo.txt", "r", encoding="utf-8") as f:
content = f.read()
print("読み取った内容:\n" + content)
存在しない場合のエラー: [Errno 2] No such file or directory: 'r_demo.txt'
読み取った内容:
一行目
二行目
‘w'(書き込み/上書き) 既存内容が消える
‘w’で開くと既存内容は即座に消去され、先頭から書き込みます。
ログの上書きや一時ファイルの初期化には適しますが、既存データを残したい用途には向きません。
サンプルコード(‘w’はトランケートする)
# 'w'はファイルを新規作成または既存を空にしてから書く
with open("w_demo.txt", "w", encoding="utf-8") as f:
f.write("旧データ\n")
# 再度'w'で開くと、旧データは消える
with open("w_demo.txt", "w", encoding="utf-8") as f:
f.write("新規データ\n")
# 読んで確認
with open("w_demo.txt", "r", encoding="utf-8") as f:
print("ファイル内容:")
print(f.read())
ファイル内容:
新規データ
‘a'(追記専用) 常に末尾に追加
‘a’は既存内容を保持し、書き込みは常に末尾へ追加します。
ログ蓄積などに向いています。
サンプルコード(‘a’は末尾に追記)
# 先に初期内容を作成
with open("a_demo.txt", "w", encoding="utf-8") as f:
f.write("先頭行\n")
# 'a'で追記していく
with open("a_demo.txt", "a", encoding="utf-8") as f:
f.write("追記1\n")
with open("a_demo.txt", "a", encoding="utf-8") as f:
f.write("追記2\n")
with open("a_demo.txt", "r", encoding="utf-8") as f:
print("ファイル内容:")
print(f.read())
ファイル内容:
先頭行
追記1
追記2
読み書き可否とカーソル位置の違い
モードごとに、開いた直後のカーソル位置が異なります。
‘r’と’r+’は先頭、’a’と’a+’は末尾、’w’と’w+’は先頭(ただし内容は消える)という違いを覚えると、読み書きの前後でseek()
が必要かどうか判断しやすくなります。
特に‘a’と’a+’は書き込み位置が常に末尾で、カーソルを動かしても書く場所は変えられません。
モード | 初期カーソル | 書き込みの反映位置 |
---|---|---|
‘r’ | 先頭 | ー |
‘w’ | 先頭 | 現在位置(上書き、既存は消去済み) |
‘a’ | 末尾 | 常に末尾(追記のみ) |
‘r+’ | 先頭 | 現在位置から上書き(挿入ではない) |
‘w+’ | 先頭 | 現在位置から上書き(既存は消去済み) |
‘a+’ | 末尾 | 常に末尾(カーソル無視) |
「上書き」は「挿入」とは異なります。
write()
はデータを差し込むのではなく、現在位置から既存のバイトを置き換えます。
途中に挿入したい場合は、自力で新しい内容を組み立ててからwrite()
するか、テンポラリファイルを使います。
‘+’付きモードの使い方(‘r+’ ‘w+’ ‘a+’)
‘r+’ 既存を保ちつつ読み書き
‘r+’は既存内容を保ちながら読み書きができます。
初期位置は先頭です。
読み取った後に書くときは、seek()
で書きたい位置へ移動しましょう。
サンプルコード(‘r+’で先頭を一部上書き)
# 事前に内容を作成
with open("rplus_demo.txt", "w", encoding="utf-8") as f:
f.write("abcdef")
# 'r+'で開いて読みつつ、先頭を上書き
with open("rplus_demo.txt", "r+", encoding="utf-8") as f:
head3 = f.read(3) # 'abc' を読む。カーソルは位置3へ
print("先頭3文字:", head3)
f.seek(0) # 先頭へ戻す
f.write("XY") # 'ab' を 'XY' に上書き(挿入ではない)
f.seek(0)
print("最終内容:", f.read())
先頭3文字: abc
最終内容: XYcdef
‘w+’ 作成/初期化して読み書き
‘w+’はファイルを新規作成または空に初期化して、読み書き両方を行えます。
読み直す前にseek(0)
が必要です。
サンプルコード(‘w+’で書いてから読む)
with open("wplus_demo.txt", "w+", encoding="utf-8") as f:
f.write("hello")
f.seek(0) # 読む前に先頭へ
print("内容:", f.read()) # => hello
f.seek(0)
f.write("HE") # 先頭から上書き -> 残りはそのまま
f.seek(0)
print("上書き後:", f.read()) # => HEllo
内容: hello
上書き後: HEllo
‘a+’ 読み込み可だが書き込みは末尾のみ
‘a+’は読み書きできるものの、書き込み位置は常に末尾です。
読むときはseek(0)
で先頭へ戻す必要があります。
サンプルコード(‘a+’はseekしても書き込みは末尾)
# 事前に内容を作成
with open("aplus_demo.txt", "w", encoding="utf-8") as f:
f.write("start\n")
with open("aplus_demo.txt", "a+", encoding="utf-8") as f:
f.seek(0) # 先頭に移動しても...
f.write("HEAD\n") # 書き込みは末尾に入る(仕様)
f.seek(0) # 読む前に先頭へ
print("内容:\n" + f.read())
内容:
start
HEAD
よくある落とし穴とseekのコツ
「読んだ直後にすぐread()しても空文字になる」のは、カーソルが末尾にあるためです。
読んだ後に再度読む場合はseek(0)
で先頭へ戻す、書いた直後に読む場合もseek()
で読みたい位置へ移動します。
もう1つの落とし穴は‘a+’ではseekしても書き込み位置が末尾のままという仕様です。
途中を書き換えたいなら'r+'
を使います。
また、write()
で短い文字列を上書きしたとき、残りの古い内容がファイル末尾に残る
ことがあります。
意図どおりに切り詰めたいときはtruncate()
を使います。
パターン: 書き換えて余りを消し、その後に読む
# 'r+'で全体を書き換える安全パターン
with open("edit_demo.txt", "w", encoding="utf-8") as f:
f.write("123456789")
with open("edit_demo.txt", "r+", encoding="utf-8") as f:
f.seek(0)
f.write("abc") # 先頭3文字を上書き -> 'abc456789'
f.truncate(3) # ファイル長を3に切り詰め -> 'abc'
f.flush() # OSへフラッシュ(即時反映したい場合)
f.seek(0)
print("確定内容:", f.read())
確定内容: abc
エラー対策とベストプラクティス(モード選択)
よくある例外(FileNotFoundError/PermissionError)
FileNotFoundError
:'r'
/'r+'
で存在しないファイルを開いたときに発生します。作成もしたい場合は'w'
/'a'
/'w+'
/'a+'
を検討します。PermissionError
: 読み取り専用ファイルや権限のない場所に書こうとしたときに発生します。保存先のパスやOSの権限を確認します。- そのほか:
IsADirectoryError
(ファイルではなくディレクトリを開いた)、UnicodeDecodeError
(テキストのエンコーディング不一致)など。
サンプルコード(例外を捕捉してメッセージ化)
def safe_read(path: str) -> str:
try:
with open(path, "r", encoding="utf-8") as f:
return f.read()
except FileNotFoundError:
return f"[見つかりません] {path}"
except PermissionError:
return f"[権限がありません] {path}"
print(safe_read("no_such_file.txt"))
[見つかりません] no_such_file.txt
データ消失を避ける判断基準
既存データを絶対に消したくないなら、’w’と’w+’は避けるのが基本です。
追記でよいなら'a'
/'a+'
、既存を保って修正するなら'r+'
を選びます。
新規作成で既存があったら失敗させたい場合は'x'
(本稿の範囲外ですが有用)もあります。
- 読み込みだけ:
'r'
またはバイナリなら'rb'
- 上書きで初期化してから使う:
'w'
/'w+'
- ログのように追記:
'a'
/'a+'
- 既存を保って一部を書き換え:
'r+'
不安ならまず’r’や’a’で開いてバックアップを取るのが安全です。
用途別のモード早見(読み込み/上書き/追記/読み書き)
目的 | 推奨モード | 理由/注意点 |
---|---|---|
既存の内容を読むだけ | ‘r'(‘rb’) | 最も安全。存在しないとエラー |
新規に書いて初期化したい | ‘w'(‘wb’) | 既存を消す。確信があるときだけ |
追記したい(ログなど) | ‘a'(‘ab’) | 既存は保ち、常に末尾に追加 |
読みながら一部を書き換えたい | ‘r+'(‘rb+’) | 既存を保つ。上書きは挿入ではない点に注意 |
初期化してから読んだり書いたり | ‘w+'(‘wb+’) | 既存を消す。読み直しはseek(0) |
追記中心だが読みもしたい | ‘a+'(‘ab+’) | 読めるが書き込みは常に末尾。読む前はseek(0) |
テキストかバイナリかはデータの性質で決めます。
文字列なら't'(デフォルト) + encoding
、画像や圧縮ファイルなら'b'
を使います。
まとめ
本記事では‘r’ ‘w’ ‘a’ と、それらに’+’を付けた6つの主要モードの違いを、読み書きの可否・既存内容の扱い・カーソル位置の観点から整理しました。
特に‘w’/’w+’は開いた瞬間に既存が消える、‘a’/’a+’は書き込みが常に末尾という2点が最大の注意点です。
読みながら書くときはseek()で位置を制御し、必要に応じてtruncate()
やflush()
も併用しましょう。
迷ったときは「データを消したくないか」「途中を書き換えたいか」「テキストかバイナリか」を基準に選ぶと、安全で分かりやすいコードになります。