一覧PG-11-1 · ファイルと外部データ

ファイルの読み書きとクローズ

作業用の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

1
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
2
with path.open("w", encoding="utf-8", newline="\n") as stream: written = stream.write("あ\nB\n") print(written, stream.closed)
4 False
3
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'
4
with path.open("a", encoding="utf-8", newline="\n") as stream: written = stream.write("C\n") print(written)
2
5
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'
6
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
7
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
8
with path.open("w", encoding="utf-8", newline="\n") as stream: print(path.stat().st_size) written = stream.write("X\n")
0
9
print(stream.closed, repr(path.read_text(encoding="utf-8")), path.stat().st_size)
True 'X\n' 2

演習完了