Python:读取和写入巨大的 Json 文件

Python: Reading and Writing HUGE Json files

我是 python 的新手。如果我没有以 pythonic 方式提问,请原谅。

我的要求如下:

  1. 我需要编写python代码来实现这个需求。

  2. 将读取 60 json 个文件作为输入。每个文件大约 150 GB。

  3. 所有 60 个 json 文件的示例结构如下所示。请注意每个文件将只有一个 json 对象。每个文件的巨大尺寸是因为那个巨大的 json 对象中包含的“array_element”数组的数量和大小。

    { "string_1":"abc", "string_1":"abc", "string_1":"abc", "string_1":"abc", "string_1":"abc", "string_1":"abc", “array_element”:[] }

  4. 转换逻辑很简单。我需要合并所有 60 个文件中的所有 array_element,并将其写入一个巨大的 json 文件。这几乎是 150GB X 60 将是输出 json 文件的大小。

我请求您帮助的问题:

  1. 阅读:计划使用“ijson”模块的ijson.items(file_object,“array_element”)。您能否告诉我 ijson.items 是否会从 json 文件中的“array_element”数组一次“产生”(即不将整个文件加载到内存中)一项?我不认为 json.load 是一个选项,因为我们无法在内存中保存如此庞大的字典。

  2. 对于写作:我打算使用ijson.item读取每个项目,然后json.dumps进行“编码”,然后使用[=68=将其写入文件] 并且不使用 json.dump,因为我无法在内存中使用如此庞大的字典来使用 json.dump。如果需要在下面显示的代码中应用 f.flush() ,您能否告诉我?据我了解,内部缓冲区已满时会自动刷新,并且内部缓冲区的大小是恒定的,不会动态增长到内存过载的程度?请告诉我

  3. 对于上面提到的增量读取和写入巨大的 json 文件,是否有更好的方法?

上述读写逻辑的代码片段:

for input_file in input_files:
    with open("input_file.json", "r") as f:
         objects = ijson.items(f, "array_element")
         for item in objects:
              str = json.dumps(item, indent=2)
              with open("output.json", "a") as f:
                   f.write(str)
                   f.write(",\n")
                   f.flush()
    with open("output.json", "a") as f:
        f.seek(0,2)
        f.truncate(f.tell() - 1)
        f.write("]\n}")

希望我问清楚了我的问题。提前致谢!!

以下程序假设输入文件的格式足够可预测,可以跳过 JSON 解析以提高性能。

根据您的描述,我的假设是:

  • 所有文件都有相同的编码。
  • 所有文件在开头的某处都有一个位置,可以找到 "array_element":[,之后文件的“有趣部分”开始
  • 所有文件在末尾某处都有一个位置,]} 标记“有趣部分”的结尾
  • 所有“有趣的部分”都可以用逗号连接并且仍然有效JSON

当所有这些点都为真时,连接预定义的 header 片段、相应的文件范围和页脚片段将生成一个大的有效 JSON 文件。

import re
import mmap

head_pattern = re.compile(br'"array_element"\s*:\s*\[\s*', re.S)
tail_pattern = re.compile(br'\s*\]\s*\}\s*$', re.S)

input_files = ['sample1.json', 'sample2.json']

with open('result.json', "wb") as result:
    head_bytes = 500
    tail_bytes = 50
    chunk_bytes = 16 * 1024

    result.write(b'{"JSON": "fragment", "array_element": [\n')

    for input_file in input_files:
        print(input_file)

        with open(input_file, "r+b") as f:
            mm = mmap.mmap(f.fileno(), 0)
            
            start = head_pattern.search(mm[:head_bytes])
            end = tail_pattern.search(mm[-tail_bytes:])

            if not (start and end):
                print('unexpected file format')
                break

            start_pos = start.span()[1]
            end_pos = mm.size() - end.span()[1] + end.span()[0]

            if input_files.index(input_file) > 0:
                result.write(b',\n')

            pos = start_pos
            mm.seek(pos)
            while True:
                if pos + chunk_bytes >= end_pos:
                    result.write(mm.read(end_pos - pos))
                    break
                else:
                    result.write(mm.read(chunk_bytes))
                    pos += chunk_bytes

    result.write(b']\n}')

如果文件格式是 100% 可预测的,您可以丢弃正则表达式并使用 mm[:head_bytes].index(b'...') 等进行 start/end 位置算法。