CSVを正式な名前へ直接書き続けると、処理の途中に別の担当者が不完全なファイルを読むことがあります。途中で失敗した場合も、正式名のまま中途半端な内容が残る構成になります。
一時ファイルへ最後まで書き、読み戻して条件を確認した後に、正式な名前へ置き換える方法があります。完成前の出力を正式名へ公開しないことが目的です。
検査後だけ正式ファイルへ置き換える
次は一時フォルダー内に、練習用の旧CSVを作って試します。利用者のファイルは対象にしません。同じ親フォルダーへ一時出力し、行数と金額を検査してからos.replaceを呼びます。
import csv
import os
from pathlib import Path
from tempfile import NamedTemporaryFile, TemporaryDirectory
def save_report(target, rows):
staged = None
try:
with NamedTemporaryFile(
mode="w", encoding="utf-8-sig", newline="",
dir=target.parent, prefix=".part-", suffix=".csv", delete=False
) as output:
staged = Path(output.name)
writer = csv.DictWriter(output, fieldnames=["ID", "金額"])
writer.writeheader()
writer.writerows(rows)
with staged.open(encoding="utf-8-sig", newline="") as source:
saved = list(csv.DictReader(source))
if len(saved) != len(rows) or any(not row["ID"] for row in saved):
raise ValueError("件数かIDを確認してください")
for row in saved:
int(row["金額"])
os.replace(staged, target)
finally:
if staged is not None:
staged.unlink(missing_ok=True)
with TemporaryDirectory() as directory:
target = Path(directory) / "report.csv"
original = "ID,金額\nOLD,50\n"
target.write_text(original, encoding="utf-8")
try:
save_report(target, [{"ID": "A", "金額": "不明"}])
except ValueError:
pass
same = target.read_text(encoding="utf-8") == original
print("失敗時に旧ファイルを維持:", same)
assert same
save_report(target, [{"ID": "A", "金額": 100}])
with target.open(encoding="utf-8-sig", newline="") as source:
result = list(csv.DictReader(source))
print("成功時の金額:", result[0]["金額"])
assert result == [{"ID": "A", "金額": "100"}]
金額が不明の入力では旧ファイルが残り、正常な入力では100のCSVへ替わります。Windows・Python 3.12.10で、この二つの結果を確認しました。練習用の一時ファイルは処理後に片付けられます。
一時ファイルを閉じてから操作する
NamedTemporaryFileを使うと、重複しにくい一時名を作れます。例はwithを抜けて書き込み口を閉じてから、読み戻しと置き換えを行います。
正式先と同じ親フォルダーへ作っているのは、別のファイルシステム間の移動を避けるためです。保存先の権限や、ほかのアプリがファイルを開いている状態などで、置き換えが失敗する可能性は残ります。
バックアップと同時実行は別に必要
os.replaceは既存の正式ファイルを置き換えます。誤った完成データを検査で見逃した場合、旧内容へ戻すための控えは別に必要です。一時保存そのものを履歴の保管と考えないようにします。
二つの処理が同時に同じ正式名を更新する競合も、このコードでは止めていません。実行を一つにする仕組みや、対象入力と結果を対応させる記録を別に用意します。電源断時の復旧や共有フォルダー上の動作まで、一律に保証する例でもありません。
実運用へ進める前に、検査失敗、書き込み権限不足、置き換え失敗のときに、旧ファイルと一時ファイルがどう残るかを、コピーした作業場所で確かめてください。
読み書きの基本はPythonのファイル操作、戻すための控えはバックアップ設計で確認できます。
