LoginSignup
11
13

More than 3 years have passed since last update.

Python3 で JSON ファイル全体を読み込まずに JSON 要素を追加する

Last updated at Posted at 2018-11-22

ログや機械学習用のデータなど、JSON ファイルにデータを保存したいが、ファイルに追記で(ファイルの末尾に)追加したい。つまり、JSON 要素を JSON ファイルの末尾に挿入したい。

しかし、ファイルが肥大化していくためファイル全体をメモリに読み込まないで追記できないか

また、1行1JSON データで完結させて追記しないで JSON 配列に追加したいのです。

これではない(一般的だけど)
{"key0": "one"}
{"key1": "two"}
{"key2": "new!"}
これ
[{"key0": "one"}, {"key1": "two"}, {"key2": "new!"}]

TL;DR

ファイル末尾の「]」を削除して JSON 配列を開けてから、「,」と JSON 要素を追加後、配列を閉じる。

original
[{"key0": "one"}, {"key1": "two"}]
ステップ1)配列を開ける("]"の削除)
[{"key0": "one"}, {"key1": "two"}
ステップ2)追記
[{"key0": "one"}, {"key1": "two"}, {"key2": "new!"}
ステップ3)配列を閉じる("]"の追加)
[{"key0": "one"}, {"key1": "two"}, {"key2": "new!"}]

TS;DR

サンプル関数

functions.py
import json

def append_json_to_file(data: dict, path_file: str) -> bool:
    with open(path_file, 'ab+') as f:              # ファイルを開く
        f.seek(0,2)                                # ファイルの末尾(2)に移動(フォフセット0)  
        if f.tell() == 0 :                         # ファイルが空かチェック
            f.write(json.dumps([data]).encode())   # 空の場合は JSON 配列を書き込む
        else :
            f.seek(-1,2)                           # ファイルの末尾(2)から -1 文字移動
            f.truncate()                           # 最後の文字を削除し、JSON 配列を開ける(]の削除)
            f.write(' , '.encode())                # 配列のセパレーターを書き込む
            f.write(json.dumps(data).encode())     # 辞書を JSON 形式でダンプ書き込み
            f.write(']'.encode())                  # JSON 配列を閉じる
    return f.close() # 連続で追加する場合は都度 Open, Close しない方がいいかも

実行例

main.py(Python>=3.6)
from functions import append_json_to_file

PATH_FILE = 'test.txt'

for i in range(10):
    data = { f'key{i}': i }              # サンプル辞書データ(>= Python3.6 f文字列)
    append_json_to_file(data, PATH_FILE) # 要素を追加

# 検証(保存ファイルのまるごと読み込み)
f_saved  = open(PATH_FILE, "r")
contents = f_saved.read()
print(contents)
f_saved.close()
main.py(Python<3.6)
from functions import append_json_to_file

PATH_FILE = 'test.txt'

for i in range(10):
    key  = 'key{num}'.format(num=i)
    data = { key: i }                    # サンプル辞書データ
    append_json_to_file(data, PATH_FILE) # 要素を追加

# 検証(保存ファイルのまるごと読み込み)
f_saved  = open(PATH_FILE, "r")
contents = f_saved.read()
f_saved.close()
print(contents)

実行結果

出力結果
[{"key0": 0} , {"key1": 1} , {"key2": 2} , {"key3": 3} , {"key4": 4} , {"key5": 5} , 
{"key6": 6} , {"key7": 7} , {"key8": 8} , {"key9": 9}]
  • オンラインで動作確認する @ paiza.IO (Python 3.6.6, Ubuntu 18.04.1 LTS)
  • macOS HighSierra(Python 3.6.0, OSX 10.13.6)
  • RaspberryPi(Python 3.4.2, Raspbian GNU/Linux 8 Jessie)

open(path_file, 'ab+')ab+

文字 意味
'r' 読み込み用に開く (デフォルト)
'w' 書き込み用に開き、まずファイルを切り詰める
'x' 排他的な生成に開き、ファイルが存在する場合は失敗する
'a' 書き込み用に開き、ファイルが存在する場合は末尾に追記する
'b' バイナリモード
't' テキストモード (デフォルト)
'+' ディスクファイルを更新用に開く (読み込み/書き込み)

Seek メソッド

fileObject.seek(offset[, from_what])

ファイルオブジェクトの位置を変更するには、f.seek(offset, from_what) を使います。ファイル位置は基準点(reference point)にオフセット値 offset を足して計算されます。
参照点は from_what 引数で選びます。from_what の値が 0 ならばファイルの 先頭から測り、1 ならば現在のファイル位置を使い、2 ならばファイルの終端を参照点として使いますfrom_what は省略することができ、デフォルトの値は 0、すなわち参照点としてファイルの先頭を使います。

(「ファイルオブジェクトのメソッド」| 入力と出力 @ Python 3.7 公式ドキュメントより)

参考文献

11
13
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
11
13