16
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

pythonでjsonのファイルを追記保存するの面倒やんけ!!(ndjsonの紹介)

Last updated at Posted at 2021-02-16

#はじめに
pythonでjson形式でファイル保存するとき,次のようにdump関数を使います.
例として,毎日の体重を記録するjsonファイルを想定します.

json形式でファイル保存
import json

# 体重をdict形式に入れる
weight_dict = {'date': '2021-01-01', 'weight': 58.3}

filename = 'weight.json'

with open(filename, 'w') as f:
    json.dump(weight_dict, f)
weight.json
{"date": "2021-01-01", "weight": 58.3}

その後,jsonファイルにさらにデータを追記したい,となったとき,どうすればよいでしょうか.
例えば,次の日の体重データを同じjsonファイルに追記したい,という場合を考えてみましょう.
対策として次のような方法があります.

#対策1:単純に追記する
上のコード実行後に次のように実行します.

対策1_追記する
weight_dict = {'date': '2021-01-02', 'weight': 58.5}

filename = 'weight.json'

with open(filename, 'a') as f:
    json.dump(weight_dict, f)
weight.json
{"date": "2021-01-01", "weight": 58.3}{"date": "2021-01-02", "weight": 58.5}

これでもよいのですが,これでは正しいjsonファイル形式になりません.
というのも,

正しいjsonファイル形式
[{"date": "2021-01-01", "weight": 58.3}, {"date": "2021-01-02", "weight": 58.5}]

なら正しい形式となるのですが,2つの(pythonでいう)辞書形式を並べただけでは,正しくなく,jsonファイルとして読み込みできません.
試しに次のようなコードを実行すると,エラーが生じます.

jsonファイル(?)を読み込み
with open(filename, 'r') as f:
    read_data = json.load(f)
エラー内容
JSONDecodeError: Extra data: line 1 column 39 (char 38)

次のようにごり押しをすればいけないこともないですが,数値データが文字列になっています.

対策1_ごり押し
with open(filename, 'r') as f:
    read_data_str = f.read()
    read_data = "[" + read_data_str.replace("\n", "").replace("}{", "},{") + "]"
    dict_data = eval(read_data)
print(dict_data)
実行結果
[{'date': '2021-01-01', 'weight': 58.3},
 {'date': '2021-01-02', 'weight': 58.5}]

#対策2:いったんファイルをすべて読みこませてから再度書き込む
はじめに,で実行したコードの後に,以下のようなコードを実行します.

対策2_読み込んで再度書き出し
weight_dict = {'date': '2021-01-02', 'weight': 58.5}

with open(filename, 'r') as f:
    read_data = json.load(f)
    
save_data = [read_data, weight_dict]

with open(filename, 'w') as f:
    json.dump(save_data, f)
weight.json
[{"date": "2021-01-01", "weight": 58.3}, {"date": "2021-01-02", "weight": 58.5}]

となり,正しいデータを追記することができました!
もちろん,この方法で問題ないですし,今回の例だったら問題ないのですが,jsonファイルのファイルサイズが大型な場合,読み込み処理に時間がかかってしまいます.

#対策3:ndjsonを使う!
ここで,json形式を使うのではなく,jsonライクな形式を使おうと考えます.
対策1では,json形式をそのまま追記していました.この方法を少し変えて,
json形式を追記するが,それぞれのデータ間に改行\nを加えるというのがndjson形式です.
ちなみにjson line形式とも呼ばれているそうです.
つまり,次のようなjsonライクなファイルで保存します.

weight.ndjson
{"date": "2021-01-01", "weight": 58.3}
{"date": "2021-01-02", "weight": 58.5}

対策1で作成したjsonファイルとの違いは,改行しているかどうか,だけてす.
また,ファイルに追記するときも,改行コードとデータをただただ末尾に追記するだけなので,すべてのデータを読み込み→追記→書き込み,をする必要はありません.

使用方法は簡単,pythonではndjsonというそのまんまのパッケージがあるので,それを使うだけです.
公式ドキュメント

pip install ndjson
対策3:ndjsonを使った場合
import ndjson

filename = 'weight.ndjson'

# 体重をdict形式に入れる
weight_dict = {'date': '2021-01-01', 'weight': 58.3}

with open(filename, 'a') as f:
    writer = ndjson.writer(f)
    writer.writerow(weight_dict)

# 追記したいデータ
weight_dict = {'date': '2021-01-02', 'weight': 58.5}

with open(filename, 'a') as f:
    writer = ndjson.writer(f)
    writer.writerow(weight_dict)
weight.ndjson
{"date": "2021-01-01", "weight": 58.3}
{"date": "2021-01-02", "weight": 58.5}

読みこみもできます.

ndjsonの読み込み
with open(filename) as f:
    data = ndjson.load(f)
実行結果
[{'date': '2021-01-01', 'weight': 58.3}, {'date': '2021-01-02', 'weight': 58.5}]

今回紹介した例では,2つのデータだけだったので,ndjsonを使うメリットがあまり実感出来なかったかもしればせんが,
例えば,for文を使って事前に長さの分からない大量のデータを書き込む場合,while True文を使って,1分おきに特定のサイトからデータを取得し,記録する,といった使い方をするときに便利です.

16
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?