如何从大文件中读取行分隔的JSON(逐行)

时间:2014-02-03 17:39:08

标签: python json parsing large-files

我正在尝试加载一个填充了JSON字符串的大文件(大小为2GB),由换行符分隔。例如:

{
    "key11": value11,
    "key12": value12,
}
{
    "key21": value21,
    "key22": value22,
}
…

我现在导入的方式是:

content = open(file_path, "r").read() 
j_content = json.loads("[" + content.replace("}\n{", "},\n{") + "]")

这似乎是一个黑客攻击(在每个JSON字符串之间添加逗号以及开头和结尾方括号以使其成为正确的列表)。

是否有更好的方法来指定JSON分隔符(换行符\n而不是逗号,)?

此外,Python似乎无法为从2GB数据构建的对象正确分配内存,有没有办法构建每个JSON对象,因为我逐行读取文件?谢谢!

7 个答案:

答案 0 :(得分:27)

此时只需读取每一行并构造一个json对象:

with open(file_path) as f:
    for line in f:
        j_content = json.loads(line)

这样,你可以加载正确的完整json对象(前提是json对象中某处或json对象中没有\n),并且在需要时创建每个对象时避免内存问题。 / p>

还有这个答案。:

https://stackoverflow.com/a/7795029/671543

答案 1 :(得分:7)

这适用于您提供的特定文件格式。如果您的格式发生变化,那么您需要更改行的解析方式。

{
    "key11": 11,
    "key12": 12
}
{
    "key21": 21,
    "key22": 22
}

只需逐行阅读,然后随时构建JSON块:

with open(args.infile, 'r') as infile:

    # Variable for building our JSON block
    json_block = []

    for line in infile:

        # Add the line to our JSON block
        json_block.append(line)

        # Check whether we closed our JSON block
        if line.startswith('}'):

            # Do something with the JSON dictionary
            json_dict = json.loads(''.join(json_block))
            print(json_dict)

            # Start a new block
            json_block = []

如果您有兴趣解析一个非常大的JSON文件而不将所有内容都保存到内存中,那么您应该查看在json.load API中使用object_hook或object_pairs_hook回调方法。

答案 2 :(得分:7)

contents = open(file_path, "r").read() 
data = [json.loads(str(item)) for item in contents.strip().split('\n')]

答案 3 :(得分:1)

不得不从AWS S3读取一些数据并解析换行符分隔的jsonl文件。我的解决方案是使用splitlines

代码:

for line in json_input.splitlines():
     one_json = json.loads(line)

答案 4 :(得分:0)

只需逐行阅读并通过流分析e 如果文件的大小超过1GB,则您的骇客技巧(在每个JSON字符串之间加上逗号,并在开头和结尾的方括号之间添加逗号以使其成为正确的列表)对内存不友好,因为整个内容将落入RAM。

答案 5 :(得分:0)

逐行阅读方法是很好的,如上面的一些答案所述。

但是,我建议在多个JSON树结构中分解为2个函数以具有更强大的错误处理。

例如,

def load_cases(file_name):
    with open(file_name) as file:
        cases = (parse_case_line(json.loads(line)) for line in file)
        cases = filter(None, cases)
        return list(cases)

parse_case_line可以封装上例中所需的键解析逻辑,例如,使用正则表达式匹配或特定于应用程序的要求。这也意味着您可以选择要解析的json键值。

此方法的另一个优点是filter在json对象的中间处理多个\n,并解析整个文件:-)。

答案 6 :(得分:0)

这扩展了Cohen的答案:

content_object = s3_resource.Object(BucketName, KeyFileName)
file_buffer = io.StringIO()
file_buffer = content_object.get()['Body'].read().decode('utf-8')

json_lines = []
for line in file_buffer.splitlines():
    j_content = json.loads(line)
    json_lines.append(j_content)

df_readback = pd.DataFrame(json_lines)

这假定整个文件都可以容纳在内存中。如果太大,则必须对其进行修改以分块读取或使用Dask