sal*_*l17 17 json split sed jq
我需要以内存高效的方式(即,无需将整个 JSON blob 读入内存)~5G将 JSON 数据的大文件 ( )拆分为带有换行符分隔的 JSON 的较小文件。每个源文件中的 JSON 数据是一个对象数组。
不幸的是,源数据不是 换行符分隔的 JSON,在某些情况下,文件中根本没有换行符。这意味着我不能简单地使用该split命令通过换行符将大文件拆分为较小的块。以下是源数据如何存储在每个文件中的示例:
带有换行符的源文件示例。
[{"id": 1, "name": "foo"}
,{"id": 2, "name": "bar"}
,{"id": 3, "name": "baz"}
...
,{"id": 9, "name": "qux"}]
Run Code Online (Sandbox Code Playgroud)
没有换行符的源文件示例。
[{"id": 1, "name": "foo"}, {"id": 2, "name": "bar"}, ...{"id": 9, "name": "qux"}]
Run Code Online (Sandbox Code Playgroud)
以下是单个输出文件所需格式的示例:
{"id": 1, "name": "foo"}
{"id": 2, "name": "bar"}
{"id": 3, "name": "baz"}
Run Code Online (Sandbox Code Playgroud)
我能够通过使用jq和split如本SO Post 中所述来实现所需的结果。由于jq流式解析器,这种方法具有内存效率。这是达到预期结果的命令:
cat large_source_file.json \
| jq -cn --stream 'fromstream(1|truncate_stream(inputs))' \
| split --line-bytes=1m --numeric-suffixes - split_output_file
Run Code Online (Sandbox Code Playgroud)
上面的命令需要~47 mins处理整个源文件。这看起来很慢,尤其是与sed可以更快地产生相同输出的相比。
以下是一些性能基准,用于显示jq与sed.
export SOURCE_FILE=medium_source_file.json # smaller 250MB
# using jq
time cat ${SOURCE_FILE} \
| jq -cn --stream 'fromstream(1|truncate_stream(inputs))' \
| split --line-bytes=1m - split_output_file
real 2m0.656s
user 1m58.265s
sys 0m6.126s
# using sed
time cat ${SOURCE_FILE} \
| sed -E 's#^\[##g' \
| sed -E 's#^,\{#\{#g' \
| sed -E 's#\]$##g' \
| sed 's#},{#}\n{#g' \
| split --line-bytes=1m - sed_split_output_file
real 0m25.545s
user 0m5.372s
sys 0m9.072s
Run Code Online (Sandbox Code Playgroud)
jq与 相比,这是较慢的处理速度sed吗?jq考虑到它在幕后进行了大量验证,速度会更慢是有道理的,但慢 4 倍似乎并不正确。jq处理此文件的速度?我更喜欢用jq来处理文件,因为我相信它可以无缝处理其他行输出格式,但鉴于我每天处理数千个文件,很难证明我观察到的速度差异是合理的。jq 的流解析器(使用 --stream 命令行选项调用的解析器)为了减少内存需求而故意牺牲速度,如下面的指标部分所示。一种取得不同平衡的工具(似乎更接近您正在寻找的东西)是jstream,其主页是 https://github.com/bcicen/jstream
在 bash 或类似 bash 的 shell 中运行命令序列:
cd
go get github.com/bcicen/jstream
cd go/src/github.com/bcicen/jstream/cmd/jstream/
go build
Run Code Online (Sandbox Code Playgroud)
将产生一个可执行文件,您可以像这样调用它:
jstream -d 1 < INPUTFILE > STREAM
Run Code Online (Sandbox Code Playgroud)
假设 INPUTFILE 包含一个(可能是巨大的)JSON 数组,上面的行为就像 jq 的一样.[],带有 jq 的 -c(紧凑)命令行选项。事实上,如果 INPUTFILE 包含 JSON 数组流,或 JSON 非标量流,情况也是如此……
对于手头的任务(流式传输数组的顶级项目):
mrss u+s
jq --stream: 2 MB 447
jstream : 8 MB 114
jq : 5,582 MB 39
Run Code Online (Sandbox Code Playgroud)
用一句话来说:
space: jstream 在内存方面是经济的,但不如 jq 的流解析器。
time: jstream 的运行速度比 jq 的常规解析器稍慢,但比 jq 的流解析器快 4 倍。
有趣的是,两个流解析器的空间*时间大致相同。
测试文件包含 10,000,000 个简单对象的数组:
[
{"key_one": 0.13888342355537053, "key_two": 0.4258700286271502, "key_three": 0.8010012924267487}
,{"key_one": 0.13888342355537053, "key_two": 0.4258700286271502, "key_three": 0.8010012924267487}
...
]
Run Code Online (Sandbox Code Playgroud)
$ ls -l input.json
-rw-r--r-- 1 xyzzy staff 980000002 May 2 2019 input.json
$ wc -l input.json
10000001 input.json
Run Code Online (Sandbox Code Playgroud)
$ /usr/bin/time -l jq empty input.json
43.91 real 37.36 user 4.74 sys
4981452800 maximum resident set size
$ /usr/bin/time -l jq length input.json
10000000
48.78 real 41.78 user 4.41 sys
4730941440 maximum resident set size
/usr/bin/time -l jq type input.json
"array"
37.69 real 34.26 user 3.05 sys
5582196736 maximum resident set size
/usr/bin/time -l jq 'def count(s): reduce s as $i (0;.+1); count(.[])' input.json
10000000
39.40 real 35.95 user 3.01 sys
5582176256 maximum resident set size
/usr/bin/time -l jq -cn --stream 'fromstream(1|truncate_stream(inputs))' input.json | wc -l
449.88 real 444.43 user 2.12 sys
2023424 maximum resident set size
10000000
Run Code Online (Sandbox Code Playgroud)
$ /usr/bin/time -l jstream -d 1 < input.json > /dev/null
61.63 real 79.52 user 16.43 sys
7999488 maximum resident set size
$ /usr/bin/time -l jstream -d 1 < input.json | wc -l
77.65 real 93.69 user 20.85 sys
7847936 maximum resident set size
10000000
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2064 次 |
| 最近记录: |