ファイルを読み込む
このレッスンで作るもの —
openとreadでテキストファイルを読み込み、1 行ずつのリストに変えるread_linesを作ります。
ファイルを読み込む
ここまでのおこづかい帳は、プログラムを終了すると記録がすべて消えていました。変数もリストも辞書も、メモリの上にしかないからです。次に起動したときも同じ記録を見られるようにするには、内容を ファイル に残す必要があります。
第 8 章では「保存できるおこづかい帳」を目指します。最初の一歩は、すでにあるファイルを読み込むところからです。
ファイルは、プログラムが終わっても消えない置き場所です。変数がメモの走り書きだとすれば、ファイルは引き出しにしまったノートにあたります。
open と read の書き方
ファイルを扱うときは、まず open でファイルを開きます。第 1 引数がファイルのパス、第 2 引数がモードです。読み込みのモードは "r" を指定します。
f = open("kakeibo.txt", "r", encoding="utf-8")
text = f.read()
f.close()open が返す f は ファイルオブジェクト と呼ばれ、ファイルを操作するための取っ手のようなものです。read を呼ぶと、中身が丸ごと 1 本の文字列として返ってきます。使い終わったら close で閉じます。
encoding="utf-8" は文字コードの指定です。日本語を含むファイルではこれを付けないと、環境によって文字化けしたりエラーになったりします。付ける癖を付けておくと事故が減ります。
読み込んだ文字列を行に分ける
read が返すのは 1 本の長い文字列です。ファイルの中に 3 行書いてあれば、改行 \n が 2 つ入った文字列になります。
text = "りんご,120,食費\nみかん,80,食費"これを 1 行ずつのリストにするには splitlines を使います。split("\n") でもほぼ同じことができますが、splitlines は末尾の改行によって空文字が増えないので安全です。
lines = text.splitlines()
# ["りんご,120,食費", "みかん,80,食費"]ファイルの最後に改行が 1 つ入っていることはよくあります。split("\n") だと最後に "" という空の要素が付いてしまい、後の処理で「品名が空の支出」が混ざる原因になります。
空文字の混入は、その場ではエラーにならず、集計の段になって初めて表面化します。取り込む入口で形をそろえておくのが一番安上がりです。
学習環境では書いてから読む
この演習では、read_lines(path, text) のようにファイルの中身も引数で受け取ります。関数の中でまず path に text を書き出し、そのうえで読み戻す形にします。
学習環境の都合で、あらかじめ用意されたファイルを置いておけないためです。手元のパソコンでは、すでにあるファイルを読むだけで構いません。書き込みの部分は次のレッスンで詳しく扱います。
動きを追ってみる
read_lines("/tmp/kakeibo.txt", "りんご,120,食費\nみかん,80,食費") を呼んだときの流れを追います。
/tmp/kakeibo.txtを書き込みモードで開き、渡された文字列をそのまま書く- 同じパスを読み込みモードで開き直す
readで"りんご,120,食費\nみかん,80,食費"を得るsplitlinesで["りんご,120,食費", "みかん,80,食費"]になる- そのリストが戻り値になる
中身が空文字 "" のときは、splitlines の結果も空リスト [] になります。「まだ 1 件も記録がない」状態が自然に表現できます。
空のときに空リストが返る、というのは地味ですが大事な性質です。呼び出す側が場合分けを書かずに
forで回せます。
よくある間違い
closeを書き忘れる — 開いたまま放置するとファイルが解放されず、書き込んだ内容が反映されないことがあります。次のレッスンで学ぶwithを使えばこの問題は消えます。- モードを間違える — 読むつもりで
"w"を渡すと、ファイルの中身が空になってしまいます。"r"が読み、"w"が書きです。 - 戻り値が文字列のまま —
readの結果をそのままreturnすると、行のリストではなく 1 本の文字列が返ります。splitlinesを通してからにします。
print(text) で確かめて満足するのもよくある落とし穴です。採点は戻り値だけを見るので、必ず return で返してください。
やってみよう
関数 read_lines(path, text) を実装してください。まず path に text を書き出し、そのうえで同じファイルを読み込み、splitlines で行のリストにして返します。
文字コードは encoding="utf-8" を指定します。text が空文字のときは空リスト [] が返る形になっていれば正解です。末尾に改行が付いた文字列を渡しても、余分な空要素が増えないことを確かめてみてください。
要件
- 関数名は read_lines で、引数は path と text の 2 つ
- 先に path を書き込みモードで開き、text をそのまま書き出す
- 同じ path を読み込みモードで開き、read で中身を取得する
- splitlines で行のリストにして return する
- open には encoding="utf-8" を指定する
入出力例
read_lines("/tmp/kakeibo_read_1.txt", "りんご,120,食費
みかん,80,食費") → ["りんご,120,食費","みかん,80,食費"]
read_lines("/tmp/kakeibo_read_2.txt", "ノート,200,日用品") → ["ノート,200,日用品"]
read_lines("/tmp/kakeibo_read_3.txt", "") → []
read_lines("/tmp/kakeibo_read_4.txt", "パン,150,食費
牛乳,180,食費
") → ["パン,150,食費","牛乳,180,食費"]
read_lines("/tmp/kakeibo_read_5.txt", "バス,220,交通費
映画,1800,娯楽
洗剤,380,日用品") → ["バス,220,交通費","映画,1800,娯楽","洗剤,380,日用品"]