ファイルに書き込む
このレッスンで作るもの — 支出の行をファイルに書き出し、その場で読み戻して確かめる
write_recordsを作ります。
ファイルに書き込む
前回はファイルを読みました。今回は書きます。おこづかい帳で言えば、アプリを終了するときに記録を残す部分にあたります。
書き込みも open から始まります。違うのはモードだけで、読みの "r" に対して書きは "w" を指定します。
f = open("kakeibo.txt", "w", encoding="utf-8")
f.write("りんご,120,食費")
f.close()write はかっこの中の文字列をファイルに書き出します。print と似ていますが、決定的な違いが 1 つあります。
writeは付けません。改行が必要なら自分で\nを書きます。
上書きモードと追記モード
"w" は 上書き のモードです。すでに中身があるファイルを "w" で開いた瞬間、中身は空になります。書く前から消えるので、開くだけで消えてしまう点に注意します。
f = open("kakeibo.txt", "w", encoding="utf-8")
f.close()これだけで、元の記録は失われます。うっかり "r" と "w" を打ち間違えると起きる事故です。
末尾に足したいときは "a" という追記モードを使います。おこづかい帳では「全件をまとめて書き直す」ほうが管理が楽なので、このコースでは "w" を使います。
| モード | 意味 |
|---|---|
"r" | 読み込む。ファイルが無いとエラー |
"w" | 書き込む。中身は消えて新しくなる |
"a" | 末尾に追記する |
複数行をまとめて書く
支出が何件もあるときは、リストの各要素を 1 行ずつ書きます。ここで役に立つのが第 7 章までに何度も使った join です。
lines = ["りんご,120,食費", "みかん,80,食費"]
text = "\n".join(lines)"\n".join(lines) は要素の あいだ だけに改行を挟むので、"りんご,120,食費\nみかん,80,食費" になります。末尾に改行は付きません。第 1 章から一貫している「末尾に改行を付けない」規則がここでも生きます。
for で 1 行ずつ write する書き方もできますが、その場合は自分で \n を足すことになり、最後の行だけ改行を付けないという面倒な場合分けが要ります。join のほうが素直です。
書いたら読み戻して確かめる
書き込みの怖いところは、失敗しても静かなこと です。パスを打ち間違えても、意図しない場所にファイルができるだけで、エラーは出ません。
そこでこの演習では、書いた直後に同じパスを読み戻し、その内容を戻り値にします。読み戻せたということは、確かに書けたということです。学習環境の都合でこの形にしていますが、実際の開発でも保存処理のテストは同じ考え方で書きます。
「書けたつもり」がいちばん危険です。読み戻して一致を確かめるまでは、保存できたとは言えません。
動きを追ってみる
write_records("/tmp/kakeibo.txt", ["りんご,120,食費", "みかん,80,食費"]) の流れを追います。
joinで"りんご,120,食費\nみかん,80,食費"を作る/tmp/kakeibo.txtを"w"で開く。中身は空になるwriteで文字列を書き出し、closeで閉じる- 同じパスを
"r"で開き、readで中身を読む "りんご,120,食費\nみかん,80,食費"が戻り値になる
リストが空 [] のときは join の結果も空文字 "" になり、中身が空のファイルができます。読み戻すと "" が返ります。
よくある間違い
writeに改行を期待する —write("りんご")を 2 回呼んでも 2 行にはならず、りんごりんごと横につながります。改行は自分で書きます。writeにリストを渡す —f.write(["a", "b"])はTypeErrorになります。writeが受け取れるのは文字列だけなので、先にjoinで 1 本にします。- 数値をそのまま渡す —
f.write(120)もTypeErrorです。金額を書くときはstr(120)で文字列にしてからにします。
"w" で開いた時点で中身が消える、という性質もよく忘れられます。大事なファイルのパスを渡す前に、一度 /tmp の適当な名前で試すのが安全です。
やってみよう
関数 write_records(path, lines) を実装してください。lines は文字列のリストです。"\n".join(lines) で 1 本の文字列にしてから path に書き出し、同じファイルを読み戻して、その内容の文字列を return します。
文字コードは encoding="utf-8" を指定します。lines が空リストのときは空文字 "" が返る形になっていれば正解です。末尾に余計な改行を足さないよう気をつけてください。
要件
- 関数名は write_records で、引数は path と lines の 2 つ
- lines を "\n".join で 1 本の文字列にしてから書き出す
- path を書き込みモードで開き、write で書き出して close する
- 同じ path を読み込みモードで開き、read の結果を return する
- open には encoding="utf-8" を指定する
入出力例
write_records("/tmp/kakeibo_write_1.txt", ["りんご,120,食費","みかん,80,食費"]) → "りんご,120,食費
みかん,80,食費"
write_records("/tmp/kakeibo_write_2.txt", ["ノート,200,日用品"]) → "ノート,200,日用品"
write_records("/tmp/kakeibo_write_3.txt", []) → ""
write_records("/tmp/kakeibo_write_4.txt", ["バス,220,交通費","映画,1800,娯楽","洗剤,380,日用品"]) → "バス,220,交通費
映画,1800,娯楽
洗剤,380,日用品"
write_records("/tmp/kakeibo_write_5.txt", ["もらいもの,0,その他"]) → "もらいもの,0,その他"