[英]Convert JSON to CSV/TSV
我正在尝试将 JSON 格式的这些数据( https://rest.kegg.jp/get/br:ko00001/json )转换为 CSV/TSV。 我已经能够在 awk 和 sed 中做到这一点,但我正在为更大的项目学习 Perl,所以在没有 JSON 模块的情况下学习这样做会很有帮助。
sed -E 's/^\t{2}"name"/\t\t"level 1"/g;s/^\t{3}"name"/\t\t\t"level 2"/g;s/^\t{4}"name"/\t\t\t\t"level 3"/g;s/^\t{5}"name"/\t\t\t\t\t"level 4"/g' json.json | awk 'BEGIN {OFS="\t"} NR > 4 {match($0, /"([^"]+)": *("[^"]*")/, a)} {tag = a[1]; val = gensub(/^"|"$/, "", "g", a[2]); f[tag] = val; if (tag == "level 4") {print f["level 1"], f["level 2"], f["level 3"], f["level 4"]}}' > table.tsv
以上是我通过 awk 和 sed 制作的。 json.json 从链接下载。
这是迄今为止我在没有 JSON 模块的 Perl 中一直在尝试的。 我想通过这种方式了解数据结构以及 Perl 的工作原理。
use strict;
my $brite_hierarchy_filepath = shift @ARGV;
open my $brite_hierarchy, '<:utf8', $brite_hierarchy_filepath or die q{Can't open $brite_hierarchy_filepath: $!\n};
while (my $line = <$brite_hierarchy>) {
next if $. == 4;
chomp $line;
$line =~ s/\A\t{2}"name"/"level_1"/;
$line =~ s/\A\t{3}"name"/"level_2"/;
$line =~ s/\A\t{4}"name"/"level_3"/;
$line =~ s/\A\t{5}"name"/"level_4"/;
my ($tag) = $line =~ /\A"(.*?)"/;
my ($value) = $line =~ /\A"level_[1-4]":"(.*?)"/;
my %field = ($tag => $value) unless $tag eq "" && $value eq "";
for (keys %field) {
print join("\t", $field{"level_1"}, $field{"level_2"}, $field{"level_3"}, $field{"level_4"}, "\n");
};
last if eof $brite_hierarchy;
};
这就是数据的简要外观。
{
"name":"ko00001",
"children":[
{
"name":"09100 Metabolism",
"children":[
{
"name":"09101 Carbohydrate metabolism",
"children":[
{
"name":"00010 Glycolysis \/ Gluconeogenesis [PATH:ko00010]",
"children":[
{
"name":"K00844 HK; hexokinase [EC:2.7.1.1]"
},
{
"name":"K12407 GCK; glucokinase [EC:2.7.1.2]"
},
{
"name":"K00845 glk; glucokinase [EC:2.7.1.2]"
...
以及 TSV 格式的所需输出。
09100 Metabolism 09101 Carbohydrate metabolism 00010 Glycolysis \/ Gluconeogenesis [PATH:ko00010] K00844 HK; hexokinase [EC:2.7.1.1]
09100 Metabolism 09101 Carbohydrate metabolism 00010 Glycolysis \/ Gluconeogenesis [PATH:ko00010] K12407 GCK; glucokinase [EC:2.7.1.2]
09100 Metabolism 09101 Carbohydrate metabolism 00010 Glycolysis \/ Gluconeogenesis [PATH:ko00010] K00845 glk; glucokinase [EC:2.7.1.2]
我总是建议使用 JSON 解析器,但如果你能保证格式永远不会改变,你确实可以把它当作一个固定的文本文件。 在生产中,您通常不能。 但如果它是一次性的,那么它肯定有效。
您粘贴到问题中的示例输入有空格,而不是制表符,因此您的代码将无法使用它。 我的也不会。 我的输入是从您的链接中复制的,并且有标签。
您的正则表达式模式似乎有点复杂。 您始终可以使用相同的琐碎模式,但只需要改变每个名称前的制表符数量即可。 诀窍是每当您找到一个不是最后一列的名称时跳到下一行,并重置第一列的整个结构。 我选择使用数组而不是哈希,因为这样更有意义,我们可以稍后在输出时join
。 最后, say
与print
类似,但带有内置换行符。
use strict;
use warnings;
use feature 'say';
my @names;
while (<DATA>) {
if ( m/^\t"name":"(.+)"/) {
undef @names;
$names[0] = $1;
next;
}
if (m/^\t\t"name":"(.+)"/) {
$names[1] = $1;
next;
}
if (m/^\t\t\t"name":"(.+)"/) {
$names[2] = $1;
next;
}
if (m/^\t\t\t\t"name":"(.+)"/) {
$names[3] = $1;
next;
}
if (m/^\t\t\t\t\t"name":"(.+)"/) {
$names[4] = $1;
say join "\t", @names;
}
}
__DATA__
{
"name":"ko00001",
"children":[
{
"name":"09100 Metabolism",
"children":[
{
"name":"09101 Carbohydrate metabolism",
"children":[
{
"name":"00010 Glycolysis \/ Gluconeogenesis [PATH:ko00010]",
"children":[
{
"name":"K00844 HK; hexokinase [EC:2.7.1.1]"
},
{
"name":"K12407 GCK; glucokinase [EC:2.7.1.2]"
},
use v5.14;
use warnings;
use open ":std", ":encoding(UTF-8)";
my @names;
while ( <> ) {
my ( $tabs, $name ) = /^\t{2}(\t*)"name": "(.*)"/
or next;
my $level = length( $tabs );
$names[ $level ] = $name;
say join "\t", @names if $level == 4;
}
不使用 JSON 解析器太可怕了。
虽然代码看起来不是很干净,但我设法创建了 TSV 格式的表格,与 sed 和 awk 生成的表格完全一样。
感谢所有关于使用模块 JSON 的信息,但是通过这种方式,我了解了更多关于在循环块之外使用变量的信息,我们可以将它存储在循环中的下一轮。
use strict;
my $brite_hierarchy_filepath = shift @ARGV;
open my $brite_hierarchy, '<:utf8', $brite_hierarchy_filepath or die q{Can't open $brite_hierarchy_filepath: $!\n};
my $previous_1;
my $previous_2;
my $previous_3;
while (my $line = <$brite_hierarchy>) {
next if $. == 4;
chomp $line;
# change accordingly to the hierarchical levels
$line =~ s/\A\t{2}"name"/"level_1"/;
$line =~ s/\A\t{3}"name"/"level_2"/;
$line =~ s/\A\t{4}"name"/"level_3"/;
$line =~ s/\A\t{5}"name"/"level_4"/;
# find the categories and put them into a hash
my ($tag) = $line =~ /\A"(.*?)"/;
my ($value) = $line =~ /\A"level_[1-4]":"(.*?)"/;
my %field = ($tag => $value) unless $tag eq "" && $value eq "";
for (keys %field) {
$previous_1 = $field{"level_1"} if $_ eq "level_1" && $field{"level_1"} ne "";
$previous_2 = $field{"level_2"} if $_ eq "level_2" && $field{"level_2"} ne "";
$previous_3 = $field{"level_3"} if $_ eq "level_3" && $field{"level_3"} ne "";
print join("\t", $previous_1, $previous_2, $previous_3, $field{"level_4"}, "\n") unless $field{"level_4"} eq "";
};
last if eof $brite_hierarchy;
};
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.