ファイルの読み書きとクローズ
作業用のworkディレクトリで日本語を含むファイルを作り、保存バイト数と追記結果を調べます。行単位の読取り、既存ファイルへの新規作成、例外時のクローズ、上書きの順に確認します。
説明
テキストファイルは文字コードを指定して文字列とバイト列を変換します。開くモードによって新規作成、上書き、追記が変わり、使用後にはファイルを閉じる必要があります。
作業用のworkディレクトリで日本語を含むファイルを作り、保存バイト数と追記結果を調べます。行単位の読取り、既存ファイルへの新規作成、例外時のクローズ、上書きの順に確認します。
1作業用の相対パス
現在の作業ディレクトリの下にworkを作り、その中のnotes.txtを対象にします。
from pathlib import Path; root = Path("work"); root.mkdir(exist_ok=True)作業用ディレクトリ; path = root / "notes.txt"パスを組み立てる; print(path, path.is_absolute(), path.exists())相対か・存在するかを確認work/notes.txtは相対パスで、現在の作業ディレクトリを基準にします。この時点でファイルはまだ存在しません。相対パスはコードのファイルがある場所を常に基準にするわけではありません。
2UTF-8で二行を書き込む
wは書込み用で、既存ファイルなら内容を切り詰めます。今回は未作成のファイルに「あ」とBの二行を書きます。
with path.open("w", encoding="utf-8", newline="\n")書込みモードと文字コード・改行 as stream:
written = stream.write("あ\nB\n")二行を保存
print(written, stream.closed)書いた文字数と開閉状態writeの戻り値4は文字数で、改行も一文字ずつ数えています。withの中ではclosedはFalseです。ここでは改行の出力をLFに指定しています。
3文字数と保存バイト数
withを抜けた後の状態と、保存したバイト列を確認します。
data = path.read_bytes()符号化済みのバイトを取得; print(stream.closed, len(data), data.hex(" "))解放・長さ・各バイト; print(repr(path.read_text(encoding="utf-8")))同じ文字コードで復号closedはTrue、保存は6バイトです。「あ」のe3 81 82、LFの0a、Bの42、LFの0aとなります。テキストのwriteが返した4と、ディスクへ保存した6バイトは単位が違います。
4aで末尾に追記する
既存の二行を残して、Cの行を追加します。
with path.open("a", encoding="utf-8", newline="\n")追記で開く as stream:
written = stream.write("C\n")末尾へ一行追加
print(written)追加した文字数追記した文字数は2です。aは末尾へ書くモードで、wのように既存内容を空にしてから書き始めるモードではありません。
5行単位で読み、改行を確認する
ファイルを反復して各行を取り出します。reprで行末の改行も見えるようにします。
with path.open("r", encoding="utf-8")読取りで開く as stream:
for number, line in enumerate(stream, 1)行番号を1から付ける:
print(number, repr(line), repr(line.rstrip("\n"))取得した行と改行除去後)三行とも末尾に改行があり、rstrip("\n")では改行だけを除いています。引数なしのstripは空白も除くため、行内データとして末尾空白が必要な場合は使い方を分けます。
6既存ファイルをxで作ろうとする
xは既存ファイルを上書きせず、新規作成だけを許すモードです。
try:
with path.open("x", encoding="utf-8")新規作成だけを許すモード as new_stream:
pass
except FileExistsError as error:既存ファイルを検出
print(f"{type(error).__name__}: {error.filename}")失敗した種類と対象名FileExistsErrorになります。既存のnotes.txtは保持されます。作成時に存在しては困る場合、先にexistsを確認してからwで開く方法より、作成操作自体に条件を付けられます。
7読取り中の例外でも閉じられるか
最初の行「あ」を整数へ変換し、ValueErrorを発生させます。withの外で例外を捕捉します。
try:
with path.open("r", encoding="utf-8") as stream:資源の寿命をwithにまとめる
int(stream.readline())読み取った文字列の変換失敗
except ValueError as error:外側で捕捉
print(type(error).__name__, stream.closed)例外と解放状態ValueErrorとclosed Trueです。withはこのファイルオブジェクトの終了処理を呼び、例外で抜ける場合も閉じます。読み取った内容を整数に変換できたという意味ではありません。
8wは開いた時点で内容を空にする
最後に上書きを行います。wで開いた直後のサイズを調べてから、新しい一行を書きます。
with path.open("w", encoding="utf-8", newline="\n")既存内容を切り詰めて開く as stream:
print(path.stat().st_size)書込み前のサイズ
written = stream.write("X\n")新しい内容開いた直後のサイズは0です。既存の三行が空になるのは、最初のwriteではなくwで開いた時点です。保存先とモードの指定を確認してから操作します。
9上書き後を読み返す
閉じられた後の内容とサイズを確認します。
print(stream.closed, repr(path.read_text(encoding="utf-8")), path.stat().st_size)解放・内容・サイズを照合Xと改行の2バイトだけになりました。読取りや追記の結果と比較すると、同じファイル名でもモードで保存内容の扱いが変わることが分かります。
まとめ
文字数とバイト数、相対パスの基準、開くモードを明示します。
withで解放をまとめ、保存した内容を読み返して確認します。
操作履歴と解説を振り返れます。
Python 3
from pathlib import Path; root = Path("work"); root.mkdir(exist_ok=True); path = root / "notes.txt"; print(path, path.is_absolute(), path.exists())work/notes.txt False False
with path.open("w", encoding="utf-8", newline="\n") as stream:
written = stream.write("あ\nB\n")
print(written, stream.closed)4 False
data = path.read_bytes(); print(stream.closed, len(data), data.hex(" ")); print(repr(path.read_text(encoding="utf-8")))True 6 e3 81 82 0a 42 0a 'あ\nB\n'
with path.open("a", encoding="utf-8", newline="\n") as stream:
written = stream.write("C\n")
print(written)2
with path.open("r", encoding="utf-8") as stream:
for number, line in enumerate(stream, 1):
print(number, repr(line), repr(line.rstrip("\n")))1 'あ\n' 'あ' 2 'B\n' 'B' 3 'C\n' 'C'
try:
with path.open("x", encoding="utf-8") as new_stream:
pass
except FileExistsError as error:
print(f"{type(error).__name__}: {error.filename}")FileExistsError: work/notes.txt
try:
with path.open("r", encoding="utf-8") as stream:
int(stream.readline())
except ValueError as error:
print(type(error).__name__, stream.closed)ValueError True
with path.open("w", encoding="utf-8", newline="\n") as stream:
print(path.stat().st_size)
written = stream.write("X\n")0
print(stream.closed, repr(path.read_text(encoding="utf-8")), path.stat().st_size)True 'X\n' 2
演習完了