[英]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.