如果您有这样的csv数据集:
name, age, gender
john, 20, male
jane, 30, female
bob, 25, male
Run Code Online (Sandbox Code Playgroud)
你能说到这个:
[ {"name": "john", "age": 20, "gender: "male"},
{"name": "jane", "age": 30, "gender: "female"},
{"name": "bob", "age": 25, "gender: "male"} ]
Run Code Online (Sandbox Code Playgroud)
仅使用jq?
我发现这篇文章展示了我正在尝试做的事情,但它使用标题字段的"手动"映射到值.我不需要/想要重命名标题字段并且有相当多的字段.我也不想每次布局更改时都要更改脚本/命令.
是否可以动态提取标题,然后使用jq one-liner将它们与值组合?
pea*_*eak 21
总之 - 是的!
jq通常非常适合文本争用,对于具有正则表达式支持的版本尤其如此.例如,在正则表达式支持下,给定问题陈述所需的修整是微不足道的.
由于jq 1.5rc1包含正则表达式支持并且自2015年1月1日起可用,因此以下程序假定版本为jq 1.5; 如果你想使它与jq 1.4一起工作,那么请看两个"For jq 1.4"评论.
另请注意,此程序无法处理CSV的一般性和复杂性.(对于更常处理CSV的类似方法,请参阅https://github.com/stedolan/jq/wiki/Cookbook#convert-a-csv-file-with-headers-to-json)
# objectify/1 takes an array of string values as inputs, converts
# numeric values to numbers, and packages the results into an object
# with keys specified by the "headers" array
def objectify(headers):
# For jq 1.4, replace the following line by: def tonumberq: .;
def tonumberq: tonumber? // .;
. as $in
| reduce range(0; headers|length) as $i ({}; .[headers[$i]] = ($in[$i] | tonumberq) );
def csv2table:
# For jq 1.4, replace the following line by: def trim: .;
def trim: sub("^ +";"") | sub(" +$";"");
split("\n") | map( split(",") | map(trim) );
def csv2json:
csv2table
| .[0] as $headers
| reduce (.[1:][] | select(length > 0) ) as $row
( []; . + [ $row|objectify($headers) ]);
csv2json
Run Code Online (Sandbox Code Playgroud)
示例(假设csv.csv是给定的CSV文本文件):
$ jq -R -s -f csv2json.jq csv.csv
[
{
"name": "john",
"age": 20,
"gender": "male"
},
{
"name": "jane",
"age": 30,
"gender": "female"
},
{
"name": "bob",
"age": 25,
"gender": "male"
}
]
Run Code Online (Sandbox Code Playgroud)
mik*_*e.f 18
yq(我写的免责声明)支持开箱即用:
yq file.csv -p=csv -o=json
Run Code Online (Sandbox Code Playgroud)
产量:
[
{
"name": "john",
" age": 20,
" gender": "male"
},
{
"name": "jane",
" age": 30,
" gender": "female"
},
{
"name": "bob",
" age": 25,
" gender": "male"
}
]
Run Code Online (Sandbox Code Playgroud)
原始 CSV 在第 2 列和第 3 列上有前导空格 - 不确定这是否是一个错误。您可以通过添加表达式来修剪它们:
yq '(... | select(tag == "!!str")) |= trim' file.csv -p=csv -o=json
Run Code Online (Sandbox Code Playgroud)
这将匹配所有字符串并修剪前导空格,产生:
yq '(... | select(tag == "!!str")) |= trim' file.csv -p=csv -o=json
Run Code Online (Sandbox Code Playgroud)
Mic*_*Hua 11
截至 2018 年,现代无代码解决方案是使用 Python 工具csvkithas csvjson data.csv > data.json.
请参阅他们的文档https://csvkit.readthedocs.io/en/1.0.2/
jq如果您的脚本必须同时调试csv和json格式,该工具包也非常方便和补充。
您可能还想查看一个名为visidata的强大工具。这是一个类似于原始海报的截屏案例研究。您还可以从生成脚本visidata
我玩了一下,想出了这个.但它可能不是最好的方式,我有兴趣看看你的尝试是什么样的,因为毕竟如果我们都找到了解决方案,我相信它会好两倍!
但我会从以下内容开始:
true as $doHeaders
| . / "\n"
| map(. / ", ")
| (if $doHeaders then .[0] else [range(0; (.[0] | length)) | tostring] end) as $headers
| .[if $doHeaders then 1 else 0 end:][]
| . as $values
| keys
| map({($headers[.]): $values[.]})
Run Code Online (Sandbox Code Playgroud)
该变量$doHeaders控制是否将顶行读作标题行.在你的情况下你想要它是真的,但我把它添加到未来的SO用户,因为,今天我吃了一顿美味的早餐,天气很好,为什么不呢?
小解释:
1). / "\n"按行拆分......
2)map(. / ", ")...和逗号(Big gotcha:在你的版本中,你会想要使用基于正则表达式的分割,因为像这样你也会在引号内用逗号分开.我只是用它,因为它很简洁,这使得我的解决方案看起来很酷吧?)
3)if $doHeaders then...这里我们创建一个字符串键或数字的数组,具体取决于第一行中元素的数量以及第一行是否为标题行
4).[if $doHeaders then 1 else 0 end:]好的,如果它是标题,那么修剪顶线
5)map({($headers[.]): $values[.]})在上面我们遍历前csv中的每一行,并将$values变量和键放入管道中.然后我们构建您想要的对象.
当然你会想要使用一些正则表达式来填补陷阱,但我希望你能在路上开始.
使用Miller(http://johnkerl.org/miller/doc/)非常简单。使用此input.csv文件
name,age,gender
john,20,male
jane,30,female
bob,25,male
Run Code Online (Sandbox Code Playgroud)
并运行
mlr --c2j --jlistwrap cat input.csv
Run Code Online (Sandbox Code Playgroud)
你将会有
[
{ "name": "john", "age": 20, "gender": "male" }
,{ "name": "jane", "age": 30, "gender": "female" }
,{ "name": "bob", "age": 25, "gender": "male" }
]
Run Code Online (Sandbox Code Playgroud)
这是 jq 的一个相当简单的“单行”版本,适用于“合理”大小的文件,对于非常大的文件,您将需要一个不使用 slurp 的版本。我对 jq 相当陌生,我确信有更好的方法可以做到这一点(也许只是增加索引值而不是存储在数据中)。如果您想让它更短且更难以阅读,您可以将“split”替换为 ./"\n" 和 ./","。注意:如果您确实需要逗号后的空格,可以在“,”上拆分,或在逗号拆分后添加|map(gsub("^\s+|\s+$";""))以修剪前导和尾随空格。
jq -Rs 'split("\n")|map(split(",")|to_entries)|.[0] as $header|.[1:]|map(reduce .[] as $item ({};.[$header[$item.key].value]=$item.value))'
Run Code Online (Sandbox Code Playgroud)
这是一个评论版本:
# jq -Rs
split("\n") | map( split(",") | to_entries ) # split lines, split comma & number
| .[0] as $header # save [0]
| .[1:] # and then drop it
| map( reduce .[] as $item ( {}; .[$header[$item.key].value] = $item.value ) )
Run Code Online (Sandbox Code Playgroud)
顶部部分非常简单:在换行符上分割数据,然后对于每个元素以逗号分割,然后 to_entries 将每个元素转换为带有键编号(0..N)的键/值条目: {键:#,值:字符串}
然后,它使用 map/reduce 获取每个元素,并将其替换为键/值对的对象,使用编号键索引回标头以获取标签。对于那些新来减少(像我一样)的第一个元素到分号是初始化“累加器”(您修改每次传递元素的东西)所以 .[...] 正在修改累加器和 $item是我们正在操作的对象。
更新:我现在有一个更好的版本,不使用 slurp,并且我们不使用 -n 选项,因为它会特殊对待第一行:
jq -R 'split(",") as $h|reduce inputs as $in ([]; . += [$in|split(",")|. as $a|reduce range(0,length) as $i ({};.[$h[$i]]=$a[$i])])'
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
11067 次 |
| 最近记录: |