json、jsonl文件格式与处理、转化方法
2024-07-18 18:30:42

前言

在机器学习领域中,.json.jsonl格式的文件是非常常见的。大量数据集都以这两种格式处理。然而,在导出这种类型文件的时候,会遇到很多问题。这篇文章就来谈一谈这两种文件格式以及如何解决相关问题。

1. 文件格式

.json格式

JSON的全称是JavaScript Object Notation,是一种以键值对存储信息的文件格式。其基本数据类型有:

  1. 数字。
  2. 字符串。
  3. 布尔值。
  4. 数组,由中括号括起。
  5. 对象:由大括号括起。
  6. 空值:null

我们随便打开一个.json文件,观察一下其结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
[
{
"text": "This is the text",
"color": "dark_red",
"bold": true,
"strikethough": true,
"clickEvent":
{
"action": "open_url",
"value": "zh.wikipedia.org"
},
"hoverEvent":
{
"action": "show_text",
"value":
{
"text": "something"
}
}
},
{
"translate": "item.dirt.name",
"color": "blue",
"bold": false,
"italic": true
}
]

熟悉Python语言的朋友们都知道,这个格式就是:由一个或多个字典组织起来的列表

值得一提的是,在.json文件中,字符串只能由双括号括起,不能由单括号括起来。此外,键也只能是字符串。

.jsonl格式

.jsonl格式的文件可以看作.json文件的一个扩展,其例子如下:

1
2
3
{"name": "John", "age": 30}
{"name": "Jane", "age": 25}
{"name": "Bob", "age": 40}

这个格式就是:多个字典,按照一行一个的排列方式排列起来的格式。同样,字符串只能由双括号括起,不能由单括号括起来。

2. 导出字典为.json格式

假设我们有这样一个字典列表:

1
a = [{"name": "w", 1:2}, {"name": "t", 3:4}]

想要将其导出为.json格式,应该怎么做呢?

1.字典转为字符串,直接导出

乍一看,a符合.json文件的格式,那么,我们会很自然的想到,直接将a转成字符串,输出到文件中不就可以了:

1
2
output_file = open("output.json", "a")
output_file.write(str(a))

然而,当我们尝试打开output.json时,会发现其并非一个合法的.json文件,此时该文件内容如下:

1
[{'name': 'w', 1: 2}, {'name': 't', 3: 4}]

该文件具有几个问题:

  1. 键不全是字符串。
  2. 文件中的字符串没有用双引号括起来。

那么该怎么办呢?

2. 修改字典转化成的字符串

可能,我们可以先将字典转化为字符串,然后将全部的单引号改为双引号,然后检查键的值。

这个方法很麻烦,此外,当键值对中出现转义的双引号时,处理会更加复杂:

1
a = [{"name": "w", 1:2}, {"name": "This is \"Q\"", 3:4}]

3. 利用json库导出

代码如下:

1
2
3
4
5
6
7
import json

a = [{"name": "w", 1:2}, {"name": "t", 3:4}]

output_file = open("output.json", "a")

json.dump(a, output_file)

这种方法简洁有效。

3. 导出字典为.jsonl格式

既然我们已经会导出为.json格式了,那么导出为.jsonl格式,无非只需要增加一个遍历输出的操作。因为.jsonl多个字典,按照一行一个的排列方式排列起来的格式

1
2
3
4
5
6
7
8
9
import json

a = [{"name": "w", 1:2}, {"name": "t", 3:4}]

output_file = open("output.jsonl", "a")

for block in a:
json.dump(block, output_file)
output_file.write("\n")

这样就可以了。

4. 中文问题

应用如上代码导出中文内容时,文件会变成:

1
{"src": "\u8fd9\u4e2a\u673a\u957f\u4eca\u5929\u7684\u98de\u884c\u4efb\u52a1\u662f\u5f00\u4e00\u67b6\u5927\u7684\u98de\u673a\u3002\n", "tgt": "The captain's mission today is to fly a large aircraft.\n", "hyp": "This sentence is already in English."}

这个时候,我们只需要向json.dump()中,增加一个ensure_ascii=False参数,结果就会变成:

1
{"src": "这个机长今天的飞行任务是开一架大的飞机。\n", "tgt": "The captain's mission today is to fly a large aircraft.\n", "hyp": "This sentence is already in English."}

5. 导入文件

我们要利用的妙妙工具是:eval()函数。这个函数可以将一个字符串当作代码处理。比如:

1
2
eval("print(\"s\")") # Output: "s"
eval("[1]") # Return [1] as List

第一个例子会在命令行中打印一个s,第二个例子则会返回一个列表,其元素只有一个1。

.json格式

调用json库中的load即可:

1
2
input_file = open("input.json", "r")
dicts = json.load(input_file)

.jsonl格式

由于.jsonl是一行一个字典,那么我们只需要:

1
2
3
4
5
actual_blocks = []
with open("input.jsonl", "r") as f:
blocks = f.readlines()
for block in blocks:
actual_blocks.append(eval(block))

先获得所有行,然后通过eval()函数,将所有字符串转换成dict即可。

迭代器

当我们有迭代.jsonl文件中全部dict的需要时,可以应用如下的迭代器:

1
2
3
4
5
6
7
8
9
10
def SR_jsonl_iterator(file_name):
assert file_name.endswith(".jsonl")
with open(file_name, "r", encoding='utf-8') as f:
while True:
one_line = f.readline()
if not one_line:
return
else:
curr_dict = eval(one_line)
yield curr_dict
Prev
2024-07-18 18:30:42
Next