我有一个 100+GB 的 json 文件,当我尝试用 jq 读取它时,我的计算机继续运行我们的 ram。有没有办法在限制内存使用的同时读取文件,或者有其他方法来读取非常大的 json 文件?
我在命令中输入的内容:
jq 'keys' fileName.json
确定包含单个 JSON 实体的非常大文件的结构的一种通用方法是运行以下查询:
jq -nc --stream -f structural-paths.jq huge.json | sort -u
其中
structural_paths.jq
包含:
inputs
| select(length == 2)
| .[0]
| map( if type == "number" then 0 else . end )
请注意,输出中的“0”表示相应位置至少有一个有效的数组索引,而不是“0”实际上是该位置的有效索引。
另请注意,对于非常大的文件,使用 jq --stream 处理整个文件可能会非常慢。
给定
{"a": {"b": [0,1, {"c":2}]}}
,上述咒语的结果将是:
["a","b",0,"c"]
["a","b",0]
如果您只是想了解有关顶层结构的更多信息,您可以将上面的 jq 程序简化为:
inputs | select(length==1)[0][0] | if type == "number" then 0 else . end
如果命令行
sort
失败,那么您可能希望通过仅将路径考虑到一定深度来限制路径数量。
如果深度不是太大,那么希望你的命令行
sort
能够管理;如果没有,那么使用命令行 uniq
至少会稍微修剪输出。
更好的选择可能是在 jq 中定义
unique(stream)
,然后使用它,如下所示:
# Output: a stream of the distinct `tostring` values of the items in the stream
def uniques(stream):
foreach (stream|tostring) as $s ({};
if .[$s] then .emit = false else .emit = true | .item = $s | .[$s]=true end;
if .emit then .item else empty end );
def spaths($depth):
inputs
| select(length==1)[0][0:$depth]
| map(if type == "number" then 0 else . end);
uniques(spaths($depth))
jq 的合适调用将如下所示:
jq -nr --argjson depth 3 --stream -f structural-paths.jq huge.json
除了避免排序成本之外,使用
uniques/1
还将保留原始 JSON 中路径的顺序。
如果要将数组路径表达式转换为“JSON Pointer”字符串(例如与
jm
或 jstream
一起使用),只需将以下内容附加到相关的 jq 程序中:
| "/" + join("/")
jq 的流解析器(使用 --stream 选项调用)通常可以处理非常非常大的文件(甚至在满足某些条件的情况下可以处理任意大的文件),但它通常非常慢并且通常非常麻烦。
在实践中,我发现诸如 jstream 和/或我自己的 jm 之类的工具在处理巨大文件时与 jq 结合使用效果非常好。 当以这种方式使用时,它们都非常易于使用,尽管安装可能有点麻烦。
不幸的是,如果您对 JSON 文件的内容一无所知,除了
jq empty
花费太长时间或失败之外,那么据我所知,没有任何 CLI 工具可以自动生成有用的模式。然而,查看文件的前几个字节通常会提供足够的信息来开始。或者您可以从 jm count
开始给出顶级对象的计数,然后从那里开始。如果顶级是 JSON 对象,jm -s | jq 'keys[]'
将为您提供顶级键的列表。
这是一个例子。假设我们确定文件 ginormous.json 的大小主要是因为它由一个非常长的顶级数组组成。然后假设 schema.jq (已在本页其他地方提到)在 pwd 中,您有希望通过运行找到信息丰富的模式:
jm ginormous.json |
jq -n 'include "schema" {source:"."}; schema(inputs)'
另请参阅 jq 以递归方式分析 JSON 对象,以获得更简单的模式推理引擎。
我在这里发布了一个相关问题:slurp、空输入和输入过滤器之间的区别
如果您的文件很大,但文件中的文档不是那么大(只有很多较小的文档),
jq -n 'inputs'
可以帮助您开始:
jq -n 'inputs | keys'
这是一个示例(带有小文件):
$ jq -n 'inputs | keys' <<JSON
{"foo": 21, "bar": "less interesting data"}
{"foo": 42, "bar": "more interesting data"}
JSON
[
"bar",
"foo"
]
[
"bar",
"foo"
]
如果您有一个千兆字节大或具有数百万个键的顶级对象,则此方法将不起作用。