Pythonでファイル操作を書くとき、
「とりあえず open() して読めたからOK」
「os と shutil を雰囲気で使っている」
そんな状態になっていませんか?
ファイルの読み書き・削除・バックアップは、
一見シンプルなのに、事故が起きやすい処理です。
- うっかり上書きしてデータが消えた
- 文字コードの違いで本番だけエラーになる
- 例外が起きてファイルが閉じられないまま残る
こうしたトラブルの多くは、
文法の問題ではなく「設計」の問題なんですよね。
Pythonには os、shutil、pathlib など、
ファイル操作のための標準ライブラリがいくつも用意されています。 特に Python 3.4 以降で導入された pathlib は、
パスをオブジェクトとして扱えるため、
安全で読みやすい設計をしやすくなりました。
この記事では、
「動くコード」ではなく「壊れにくいファイル操作設計」をテーマに、
- 安全な読み書きの考え方
- 削除・バックアップで事故を防ぐ設計
- 標準ライブラリだけでできる実務レベルの実装
を、できるだけ分かりやすく整理していきます。
初心者の方には「なぜそう書くのか」が腑に落ちるように、
中級者以上の方には「設計を見直すヒント」になるように、
やさしく丁寧に解説していきますね 😊
それではまず、
Pythonのファイル操作がなぜ設計で差がつくのかから見ていきましょう。
論点・背景:Pythonのファイル操作はなぜ「設計」で差がつくのか
Pythonのファイル操作は、
サンプルコードを見るととても簡単に書けるように見えます。
with open("sample.txt", "r") as f:
data = f.read()
でも実務や少し大きなスクリプトになると、
「思ったより難しい…」と感じる場面が一気に増えてきます。
- ファイルが存在しなかったらどうする?
- 途中で例外が起きたら中途半端な状態にならない?
- WindowsとMacで同じように動く?
- 削除や上書きをしても本当に安全?
こうした悩みは、
Pythonの文法を知らないから起きるわけではありません。
多くの場合、ファイル操作を「処理」としてだけ見ていることが原因です。
ファイル操作は、プログラムの中では
I/O(外部とのやり取り)にあたります。
I/Oには次のような特徴があります。
- 失敗する可能性が常にある
- 環境(OS・権限・文字コード)に影響されやすい
- 一度壊すと取り返しがつかないことがある
つまりファイル操作は、
「成功する前提」で書く処理ではないんですね。
さらにPythonには、os、os.path、shutil、pathlib など、
似た役割を持つライブラリが複数存在します。
その結果、
- 場所によって書き方がバラバラになる
- 文字列パスとPathオブジェクトが混在する
- 「どれを使うのが正解か」分からなくなる
という状態に陥りやすくなります。
そこで重要になるのが、
「どのライブラリを、どんな役割で使うか」を先に決める設計です。
この記事では、
- パス操作は
pathlibを軸に考える - コピー・移動・アーカイブは
shutilに任せる - ファイル操作は必ず失敗する前提で設計する
という考え方をベースに話を進めていきます。

まずは次の章で、
安全なファイル操作を書くための基本設計ルールを整理していきましょう。
基本設計:安全なファイル操作の原則
ファイル操作で一番大切なのは、
「必ず成功する」と思い込まないことです。
ファイルは、
- 存在しないかもしれない
- 途中で削除されるかもしれない
- 権限が足りないかもしれない
- 文字コードが想定と違うかもしれない
こんな不確実な前提の上にあるので、
設計段階で“失敗しても壊れない形”を作る必要があります。
① コンテキストマネージャ(with文)は必須
Pythonでファイルを扱うなら、with open(...) は書き方の好みではなく設計ルールです。
with open("data.txt", "r", encoding="utf-8") as f:
text = f.read()
この書き方をすると、
- 処理が正常終了した場合
- 途中で例外が発生した場合
どちらでも確実にファイルが閉じられます。
逆に f.close() を手動で書く設計は、
例外が起きた瞬間にリソースリークを引き起こします。
「閉じ忘れを防ぐ」というより、
閉じる責任を人間からPythonに渡す感覚ですね。
② エンコーディングは必ず明示する
テキストファイルを開くときは、encoding="utf-8" を必ず指定しましょう。
with open("log.txt", "w", encoding="utf-8") as f:
f.write("ログ出力")
エンコーディングを省略すると、
- Windowsでは cp932
- Mac / Linuxでは utf-8
のように、
環境ごとに挙動が変わる可能性があります。
「自分のPCでは動くのに本番で落ちる」
この手のトラブル、かなり多いです…。
設計としては、
- テキスト → UTF-8で統一
- バイナリ →
rb/wbを使う
と決め打ちしてしまうのが一番安全です。
③ ファイル操作は「失敗する前提」で書く
安全なファイル操作では、
例外処理はオマケではありません。
たとえば、
- 読み込み前に存在チェックをする
- 上書き前にバックアップを取る
- 削除前に対象をログに残す
といった一手間が、
事故を未然に防いでくれます。
このあたりは後半の章で、
削除・バックアップ設計とあわせて詳しく見ていきますね。

次は、
実際のコードを通して「ファイルの読み書きとデータ保存」を整理していきましょう。
手順①:ファイルの読み書きとデータ保存
ここからは、
実際によく使うファイル操作を、設計の視点で整理していきます。
まずは一番基本になる、
ファイルの読み書きからです。
① ファイルを開くときに考えるべきこと
open() にはモード指定がありますが、
「何となく使う」のは危険です。
"r":読み込み専用(存在しないとエラー)"w":書き込み専用(既存ファイルは上書き)"a":追記(末尾に追加)"b":バイナリモード
特に注意したいのが "w" モードです。
ファイルが存在すると、中身は即座に消えます。
この仕様を理解せずに使うと、簡単に事故が起きます。
設計としては、
- 既存ファイルを消したくない →
"a"や存在チェック - 本当に上書きしたい → バックアップを取ってから
"w"
という判断を、
コードを書く前に決めておくのが大切です。
② 読み込み処理の基本パターン
ファイルの読み込みは、用途によって使い分けます。
with open("data.txt", "r", encoding="utf-8") as f:
text = f.read()
小さなファイルなら read() で問題ありませんが、
サイズが大きい場合は注意が必要です。
1行ずつ処理したいときは、
with open("data.txt", "r", encoding="utf-8") as f:
for line in f:
print(line.strip())
この書き方なら、
メモリを無駄に使わずに処理できます。
③ 書き込み処理の基本パターン
書き込みも同様に、
「どこに、どんな影響が出るか」を意識します。
with open("output.txt", "w", encoding="utf-8") as f:
f.write("結果を書き込む")
この1行でも、
- 既存ファイルは消える
- 例外が起きたら途中までの内容になる可能性がある
というリスクがあります。
実務では、
- 一時ファイルに書く
- 最後に置き換える
といった設計がよく使われます。
④ 構造化データの保存はJSONを使う
数値・リスト・辞書など、
構造を持ったデータを保存したい場合は、 JSON形式がとても便利です。
import json
data = {
"name": "sample",
"count": 3
}
with open("data.json", "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2)
ensure_ascii=False を指定すると、
日本語がそのまま保存されます。
読み込むときは、
with open("data.json", "r", encoding="utf-8") as f:
data = json.load(f)
これだけで、
元のPythonオブジェクトが復元されます。
設定ファイルや中間データ保存では、
「とりあえずJSON」が安全な選択になることが多いです。

次は、
ディレクトリの作成・検索・一覧取得について見ていきましょう。
手順②:ディレクトリ管理と検索
ファイル操作に慣れてくると、
次に必ず出てくるのがディレクトリの扱いです。
「フォルダがなければ作る」
「特定のファイルをまとめて処理する」
こうした場面はとても多いですよね。
ここでは、
pathlibを軸にした安全で読みやすい設計を見ていきます。
① ディレクトリを作成する
ディレクトリ作成には、Path.mkdir() を使うのがおすすめです。
from pathlib import Path
dir_path = Path("data")
dir_path.mkdir()
ただし、このままだと
すでに存在している場合にエラーになります。
実務では次の指定をセットで使うことが多いです。
dir_path.mkdir(parents=True, exist_ok=True)
parents=True:中間ディレクトリもまとめて作成exist_ok=True:すでに存在してもエラーにしない
この2つを指定しておくと、
何度実行しても安全なコードになります。
② ディレクトリ内の一覧を取得する
フォルダの中身を取得したい場合は、Path.iterdir() を使います。
for path in dir_path.iterdir():
print(path)
iterdir() はイテレータを返すため、
ファイル数が多くてもメモリ効率が良いのが特徴です。
ファイルかディレクトリかを判定することもできます。
for path in dir_path.iterdir():
if path.is_file():
print("file:", path.name)
elif path.is_dir():
print("dir:", path.name)
③ パターンマッチング(glob)で検索する
特定の拡張子だけを処理したい場合は、glob() が便利です。
for path in dir_path.glob("*.txt"):
print(path)
さらに、
サブディレクトリも含めて検索したい場合は、
for path in dir_path.glob("**/*.txt"):
print(path)
** を使うことで、
再帰的な検索ができます。
大量ファイルを扱う場合でも、glob() は必要なものだけを順に返してくれるため、 安全な設計がしやすいです。
④ os系APIとの付き合い方
os.listdir() や os.scandir() も使えますが、
パスが文字列になるため、
- 結合ミスが起きやすい
- 可読性が下がりやすい
というデメリットがあります。
設計としては、
- パス操作・判定 →
pathlib - コピー・削除などの処理 →
shutil/os
と役割を分けるのが分かりやすいです。

次は、
バックアップやアーカイブをどう設計するかを見ていきましょう。
手順③:バックアップとアーカイブ操作
ファイル操作で一番ヒヤッとするのが、
コピー・移動・上書きの処理です。
ここを雑に書くと、
「気づいたら元データが消えていた…」
なんてことが普通に起こります。
この章では、
事故を前提にしたバックアップ設計を整理していきます。
① コピーと移動は役割を理解して使う
ファイルのコピー・移動には、shutil モジュールを使います。
まずはコピーです。
import shutil
shutil.copy2("data.txt", "backup/data.txt")
copy2() を使うと、
- ファイルの中身
- 更新日時・アクセス日時などのメタデータ
も一緒にコピーされます。
バックアップ用途では、copy() より copy2() を使うのが基本です。
次に移動です。
shutil.move("data.txt", "archive/data.txt")
move() は、
- 同一ファイルシステム → リネーム
- 別ファイルシステム → コピー後に元を削除
という挙動になります。
つまり、
内部では「削除」が絡む可能性があるということです。
重要なデータでは、
移動前にバックアップを取る設計をおすすめします。
② バックアップ設計の基本パターン
安全なバックアップ処理では、
次の順番を意識します。
- 元ファイルの存在確認
- バックアップ先ディレクトリの作成
copy2()で複製- 本処理(上書き・移動・削除)
「先にコピーしてから触る」
これだけで、事故率は一気に下がります。
③ アーカイブ(ZIP / TAR)を作成する
複数のファイルやディレクトリを、
まとめて保管したい場合は、アーカイブ化が便利です。
Pythonでは shutil.make_archive() を使います。
import shutil
shutil.make_archive(
base_name="backup_20240101",
format="zip",
root_dir="data"
)
この1行で、
dataディレクトリ以下をまとめてbackup_20240101.zipを作成
してくれます。
format には、
"zip""tar""gztar"
などが指定できます。
バックアップ用途なら、
扱いやすい zip が無難ですね。
④ バックアップは「設計」で決まる
バックアップ処理は、
「一度書いて終わり」ではありません。
- いつ取るのか
- どこに置くのか
- どれくらい残すのか
を先に決めておかないと、
気づいたら意味のないバックアップになります。
ファイル操作は、
「消す前に守る」が基本ルールです。

次は、
削除処理と後処理の設計を見ていきましょう。
手順④:削除と後処理(事故を防ぐ設計)
削除処理は、
ファイル操作の中で一番取り返しがつきません。
読み書きやコピーは失敗してもやり直せますが、
削除だけは「消えたら終わり」になりがちです。
だからこそ削除は、
コードを書く前に設計で守る必要があります。
① ファイル削除とディレクトリ削除の違い
まずは、
削除対象によって使う関数を明確に分けましょう。
- ファイル削除 →
Path.unlink()/os.remove() - 空ディレクトリ削除 →
Path.rmdir()/os.rmdir() - 中身ごと削除 →
shutil.rmtree()
特に shutil.rmtree() は、
フォルダ以下を丸ごと消します。
この関数を無条件で呼ぶ設計は、
かなり危険です。
② 削除前に必ずやるべきチェック
削除処理を書くときは、
次の確認を習慣にしましょう。
- 本当に存在しているか
- 想定したパスか
- ファイルかディレクトリか
from pathlib import Path
path = Path("data.txt")
if path.exists() and path.is_file():
path.unlink()
たったこれだけでも、
誤削除の確率は大きく下がります。
③ 「論理削除」という考え方
実務では、
いきなり削除しない設計もよく使われます。
- 削除予定フォルダへ移動する
- ファイル名に
.bakを付ける - 日時付きフォルダに退避させる
これはいわゆる論理削除です。
「削除」と言いつつ、
実際は安全な場所に移しているだけなので、 復元が可能です。
自動化スクリプトや定期バッチでは、
この考え方がとても重要になります。
④ 後処理とログの重要性
削除や移動を行ったら、
何をしたかを必ず残すようにしましょう。
- 削除したファイル名
- 実行日時
- エラーが起きた場合の内容
これらがログに残っているだけで、
トラブル対応の難易度が激減します。
ファイル操作は、
「やった後の説明責任」まで含めて設計です。

次は最後に、
効率的で安全な設計のための補足Tipsをまとめていきましょう。
補足:効率的で安全な設計のためのTips
ここまでで、
読み書き・ディレクトリ管理・バックアップ・削除まで一通り見てきました。
最後に、
実務で差がつきやすい補足ポイントをまとめておきますね。
① 一時ファイル・一時ディレクトリを活用する
処理の途中でしか使わないデータは、
一時ファイルとして扱うのが安全です。
import tempfile
with tempfile.TemporaryDirectory() as tmp_dir:
print(tmp_dir)
# この中で作ったファイルは処理終了時に自動削除
この方法なら、
- 削除忘れが起きない
- 途中で例外が起きても後片付けされる
といったメリットがあります。
「後で消す前提のデータ」は、
最初から消える設計にしておくと楽です。
② ファイルのメタデータを活用する
ファイルサイズや更新日時が必要な場面も多いですよね。
from pathlib import Path
path = Path("data.txt")
stat = path.stat()
print(stat.st_size) # ファイルサイズ
print(stat.st_mtime) # 更新日時(UNIX時間)
これを使うと、
- 一定サイズ以上のファイルだけ処理する
- 更新日時が新しいものだけ対象にする
といった設計が可能になります。
③ ファイル操作はロジックと分離する
ファイル操作は、
プログラムの「端」に追いやるのが基本です。
計算や判定ロジックと、
ファイルI/Oが混ざると、
- テストしにくい
- 修正しづらい
- 事故が起きやすい
という状態になります。
「読む」「書く」は別関数に分ける。
それだけでもコードはかなり安全になります。
まとめ
Pythonのファイル操作は、
文法自体はとてもシンプルです。
でも実際には、
設計を意識しないと事故が起きやすい領域でもあります。
- with文でリソースを確実に管理する
- encodingは必ず明示する
- pathlibを軸にパス操作を統一する
- 消す前に守る(バックアップ・論理削除)
このあたりを意識するだけで、
ファイル操作のコードは一気に壊れにくくなります。
小さなスクリプトほど、
「あとで直せばいいや」が積み重なりがちです。
だからこそ、
最初から安全な設計で書くクセをつけておくと、 あとで自分を助けてくれますよ 😊
あわせて読みたい
- Pythonの境界設計とは?I/Oとロジック分離で“変更に強い”コードにする方法
- PythonのI/Oが散らかる原因と直し方|実例で学ぶ「境界」の分離設計
- Python初心者が「標準ライブラリだけ」でどこまで戦えるか完全マップ
参考文献
- Python公式ドキュメント:入力と出力(ファイルの読み書きの基本)
- Python公式ドキュメント:pathlib ― オブジェクト指向のファイルシステムパス
- Python公式ドキュメント:os ― オペレーティングシステムインターフェース
- Python公式ドキュメント:os.path ― 共通パス名操作
- Python公式ドキュメント:shutil ― 高水準のファイル操作
- Real Python:Working With Files in Python
よくある質問(Q&A)
- Qpathlibだけ使えばosは不要ですか?
- A
多くのケースでは
pathlibだけで十分です。 ただし、環境変数や低レベルなOS操作ではosを併用する場面もあります。
- Qファイル操作で一番事故が多いポイントは?
- A
上書きと削除です。 特に
"w"モードとshutil.rmtree()は、 設計なしで使うと危険です。
- Qバックアップ処理はどこまで作り込むべき?
- A
「自動で消える可能性がある処理」には、 最低限バックアップを入れるのがおすすめです。 完璧よりも、戻せる設計を優先しましょう。










※当サイトはアフィリエイト広告を利用しています。リンクを経由して商品を購入された場合、当サイトに報酬が発生することがあります。
※本記事に記載しているAmazon商品情報(価格、在庫状況、割引、配送条件など)は、執筆時点のAmazon.co.jp上の情報に基づいています。
最新の価格・在庫・配送条件などの詳細は、Amazonの商品ページをご確認ください。