CSV形式で保存
このレッスンで作るもの — 辞書のリストを「品名,金額,カテゴリ」の行に変換する
to_csv_linesを作り、保存に適した形を手に入れます。
CSV 形式で保存する
ファイルに書けるのは文字列だけです。ところがおこづかい帳の記録は、第 7 章で決めたとおり辞書のリストです。
[{"name": "りんご", "price": 120, "category": "食費"}]これをそのままファイルに書くことはできません。辞書を 1 行の文字列にする決まりごと を自分で決める必要があります。
もっとも広く使われている決まりが CSV です。Comma Separated Values の頭文字で、値をカンマで区切って並べるだけの素朴な形式です。
りんご,120,食費
みかん,80,食費1 行が 1 件、カンマの区切りで 3 つの項目が並びます。表計算ソフトでもそのまま開けるので、保存先として扱いやすい形です。
保存の形式は、あとから読み戻せることがすべてです。凝った形にするより、区切りが一目で分かる形を選ぶほうが長持ちします。
join で行を組み立てる
1 件の辞書を 1 行にするには join を使います。前回までは行と行を "\n" でつなぎましたが、今回は項目と項目を "," でつなぎます。
record = {"name": "りんご", "price": 120, "category": "食費"}
line = ",".join([record["name"], str(record["price"]), record["category"]])結果は "りんご,120,食費" です。ここで大事なのが str(record["price"]) の部分です。price は int なので、join にそのまま渡すと TypeError になります。join がつなげるのは文字列だけです。
全件を変換するには for で回してリストに append します。
def to_csv_lines(records):
lines = []
for record in records:
lines.append(",".join([record["name"], str(record["price"]), record["category"]]))
return lines第 7 章で学んだリスト内包表記でも書けます。どちらでも構いません。
split で読み戻す
join の逆が split です。"," で分割すると、3 つの文字列のリストに戻ります。
name, price, category = "りんご,120,食費".split(",")第 7 章のタプルアンパックがそのまま使えます。ただし price はこの時点では文字列の "120" です。数値として計算に使うには int(price) が要ります。
書き出すときに
strで文字列にした値は、読み戻すときにintで数値に戻します。行きと帰りが対になっている、と覚えると迷いません。
動きを追ってみる
to_csv_lines に 2 件の記録を渡したときの流れを追います。
- 空のリスト
linesを用意する - 1 件目の
{"name": "りんご", "price": 120, "category": "食費"}を取り出す - 3 つの値を
","でつないで"りんご,120,食費"を作り、appendする - 2 件目も同じように処理して
"みかん,80,食費"を追加する ["りんご,120,食費", "みかん,80,食費"]が戻り値になる
records が空リストのときは for が 1 度も回らず、空リストがそのまま返ります。「まだ 1 件も記録がない」場合に自然に対応できます。
よくある間違い
strを付け忘れる —",".join([name, price, category])のようにintを混ぜるとTypeErrorになります。エラーメッセージは sequence item 1 と、何番目が悪いかを教えてくれます。- 区切り文字を混同する — 項目の区切りは
","、行の区切りは"\n"です。両方joinで書くので取り違えやすい部分です。 - 品名にカンマが入る —
"りんご, みかんセット"のようにカンマを含む品名を保存すると、読み戻したときに項目数が合わなくなります。このコースでは品名にカンマを使わない前提にしますが、実務では標準ライブラリのcsvを使ってこの問題を避けます。
金額を str にする位置を間違えて str(record) としてしまうのも定番です。辞書全体が文字列になり、まったく違う行ができます。
やってみよう
関数 to_csv_lines(records) を実装してください。records は {"name": ..., "price": ..., "category": ...} の形の辞書のリストです。1 件を "品名,金額,カテゴリ" の文字列にして、その並びをリストで返します。
金額は str で文字列にしてから join します。区切りはカンマだけで、前後に空白は入れません。records が空リストのときは空リスト [] が返る形になっていれば正解です。
要件
- 関数名は to_csv_lines で、引数は records の 1 つ
- 1 件を "品名,金額,カテゴリ" の順にカンマでつなぐ
- 金額は str で文字列にしてから join する
- カンマの前後に空白を入れない
- records が空リストのときは空リストを返す
入出力例
to_csv_lines([{"category":"食費","name":"りんご","price":120},{"category":"食費","name":"みかん","price":80}]) → ["りんご,120,食費","みかん,80,食費"]
to_csv_lines([{"category":"日用品","name":"ノート","price":200}]) → ["ノート,200,日用品"]
to_csv_lines([]) → []
to_csv_lines([{"category":"その他","name":"もらいもの","price":0}]) → ["もらいもの,0,その他"]
to_csv_lines([{"category":"交通費","name":"バス","price":220},{"category":"娯楽","name":"映画","price":1800},{"category":"日用品","name":"洗剤","price":380}]) → ["バス,220,交通費","映画,1800,娯楽","洗剤,380,日用品"]