使用 Python 即时处理 .json 文件 (read/write/change)

Working on .json files (read/write/change) on-the-fly with Python

最近我开始重写我以前写的游戏。我主要关心的是尝试解决它在单个文件上运行的主要问题(导致崩溃和数据溢出)。

在概念化更改时,我决定使用 .jsons 来即时存储数据(作为“自动保存”和 material 用于用户定期保存)。我尝试使用 json 库来创建可以让我轻松读取和写入数据的函数,方法如下:

read specific variable from .json -> operate on it, if needed -> overwrite only that specific part of .json file

遗憾的是,无论我采用何种方式,我什至无法读取 .json 文件,更不用说编辑值了。到目前为止,我发现的所有“json.dump()”示例都试图将新行写入 .json 或替换文件,而不是简单地编辑特定值。我觉得在游戏的每一回合都重写整个文件根本没有效率。

到目前为止使用的代码(2 个变体,分别导致没有控制台输出或使用 _io.TextIOWrapper,我不知道如何将其转换为有用的数据):

def json_read(path, element):
  import json
  with open(path) as json_file:
    data = json.load(json_file)
    data = dict(data[element])
    print (data)
def json_read(path, element):
  import json
  json_file = open(path, "r")
  print (json.load(json_file))

我想要实现的主要目的是使函数对四个参数进行操作,简化形式如下所示:

def json_change(path, element, change_type, change_value):
  if change_type == "replacement":
    #replace .json variable [element] value with [change_value]
  elif change_type == "maths":
    #do mathematic equation (.json variable value [element] + [change_value]) and write its result back
  elif change_type == "var_addition":
    #write new variable called [element] in the end of .json file with value of [change_value]

另一个函数只是尝试从(路径、元素)参数中读取文件,它将 return 值供 json_change 或其他元素进一步使用。这个我已经尝试介绍了,如上所示。

我建议您只使用 json 库。读取文件一次并偶尔保存一次,假设每五分钟一次。 也许您想在单例模式中创建一个新的 class。这将确保您拥有一个保存当前最新信息的对象,并且只有该对象可以更新文件。

这里是关于这个 class 可能是什么样子的快速草图:

# SaveFileHandler.py

import json


class _SaveFileHandler:
    def __init__(self):
        self._data = {}
        self._path = ""

    def read(self, path):
        self._path = path
        with open(path) as file:
            self._data = json.load(file)

    def write(self):
        with open(self._path) as file:
            json.dump(self._data, file)

    # now for every value you want data to have do the following
    # it allows you to check the value for errors and makes the usage more readable
    @property
    def key(self):
        return self._data["key"]

    @key.setter
    def key(self, value):
        self._data["key"] = value


# the is the only object you will ever use
save_file = _SaveFileHandler()

您也可以直接在init中读取存档,这样可以确保您的数据始终可用。

当我自己找到解决方案时,正是我想要的方式,这是我最终使用的代码。

首先,我决定做两个具有相似系统但服务于不同目的的功能(数据的一般保存和加载系统;将只显示保存一个,因为它们仅在使用的目录上有所不同)。 参数如下所示:

  • name = 玩家名称(保存在“saves/X”文件夹中)
  • category = .json 使用的文件(有四个,每个用于保存的不同方面(stats/quests/world changes/inventory)
  • dict_type = 决定返回值是包含来自保存的所有“变量”(真)还是仅选择的“变量”(假,默认)
  • element = 特定的“变量”(如果 dict_type = True,可以是任何东西)

所以在这里我只是让函数读取特定文件并返回字典或“变量”本身。我还为 JSONDecoreError 制作了安全门,所以如果 .json (出于某种原因)为空,它将打印出来。

def save_read(name, category, element, dict_type=False):
  import json
  final_path = "saves/" + name + "/in_use/" + category + ".json"
  data = {}
  try:
    with open (final_path) as json_file:
      data = json.load(json_file)
    if dict_type == False:
      return data[element]
    else:
      return data
  except json.decoder.JSONDecodeError:
    print ("JSON File: " + final_path + " does not have any arguments. Skipping.")

下面的代码用于更详细地管理保存文件中的“变量”。它再次检查玩家名称、类别和元素(都与上面相同),但也有参数:

  • change_type = 解释程序我们想用函数 运行ning 做什么(替换值,使其相对变化,或者如果需要新的甚至添加新的“变量”)
  • change_value = 我们使用的值,通常是int、str或boolean

因此,根据“change_type”参数,“change_value”用于不同的目的(或者根本不使用,如果我们使用 save/load,在底部解释这个答案)。

def save_change(name, category, element, change_type, change_value, in_use=True):
  #in_use=True is for all data elements within game, =False for saving game, since it uses different directory
  import json
  if in_use == True:
    final_path = "saves/" + name + "/in_use/" + category + ".json"
 
    #simply replacing value with new one (usually for string variables)
    if change_type == "replace":
      temp_dict = save_read(name, category, element, True)
      temp_dict[element] = change_value
      with open (final_path,'w') as file:
        json.dump(temp_dict, file, indent = 2)
 
    #math is intended to change value with +/- (use negative value to make it smaller)
    elif change_type == "math":
      temp_dict = save_read(name, category, element, True)
      temp_dict[element] = temp_dict[element] + change_value
      with open (final_path,'w') as file:
        json.dump(temp_dict, file, indent = 2)

    #math, but for (*) [rare case]
    elif change_type == "math*":
      temp_dict = save_read(name, category, element)
      temp_dict[element] = temp_dict[element] * change_value
      with open (final_path,'w') as file:
        json.dump(temp_dict, file, indent = 2)

    #math, but for (/) [rare case]
    elif change_type == "math/":
      temp_dict = save_read(name, category, element)
      temp_dict[element] = temp_dict[element] / change_value
      with open (final_path,'w') as file:
        json.dump(temp_dict, file, indent = 2)

    #var_add is intended to be dict; can be useful with version_updater especially
    elif change_type == "var_add":
      temp_dict = save_read(name, category, element, True)
      temp_dict.update (change_value)
      with open (final_path,'w') as file:
        json.dump(temp_dict, file, indent = 2)

    #var_add, but for deleting; change_value can be anything in this case
    elif change_type == "var_del":
      temp_dict = save_read(name, category, element, True)
      temp_dict.remove (element)
      with open (final_path,'w') as file:
        json.dump(temp_dict, file, indent = 2)

    #for loading the game (uses load_read)
    elif change_type == "game_load":
      temp_dict = load_read(name, category, element, True)
      with open (final_path,'w') as file:
        json.dump(temp_dict, file, indent = 2)

  elif in_use == False:
    #for game saving | 'element' and 'change_value' can be anything
    if change_type == "game_save":
      final_path = "saves/" + name + "/" + category + ".json"
      temp_dict = save_read(name, category, element, True)
      with open (final_path,'w') as file:
        json.dump(temp_dict, file, indent = 2)

如果您想查看完整系统的精确代码(截至目前),也可以在 latest working commit on GitHub 上查看。它使用:

  • system/json_manag.py - 用于一般管理(通信游戏<->“使用中保存”)
  • system/save_system/save_load.py - 对于完整的 save/load 机制,运行 由玩家手动

它只是覆盖存档。jsons 或“游戏当前使用”。jsons,取决于它是加载还是保存。