AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / unix / 问题 / 765176
Accepted
jesse_b
jesse_b
Asked: 2023-12-19 22:04:06 +0800 CST2023-12-19 22:04:06 +0800 CST 2023-12-19 22:04:06 +0800 CST

将表转换为 json

  • 772

我有一个很大的数据表,我想将其转换为 json,并且不确定像 jq、mlr 或类似的工具是否能够执行这样的任务,而不必求助于我糟糕的 awk 技能。

样本表:

Balance_sheet for AAPL:

                                                        2023-09-30      2022-09-30      2021-09-30      2020-09-30
Treasury Shares Number                                         0.0             NaN             NaN             NaN
Ordinary Shares Number                               15550061000.0   15943425000.0   16426786000.0   16976763000.0

首选输出:

{
    "Balance_sheet for AAPL": {
        "Treasury Shares Number": {
            "2023-09-30": "0.0",
            "2022-09-30": "NaN",
            "2021-09-30": "NaN",
            "2020-09-30": "NaN"
        },
        "Ordinary Shares Number": {
            "2023-09-30": "15550061000.0",
            "2022-09-30": "15943425000.0",
            "2021-09-30": "16426786000.0",
            "2020-09-30": "16976763000.0"
        }
    }
}

以下格式也可以使用,但不太理想:

{
    "Balance_sheet for AAPL": {
        "2023-09-30": {
            "Treasury Shares Number": "0.0",
            "Ordinary Shares Number": "15550061000.0"
        },
        "2022-09-30": {
            "Treasury Shares Number": "NaN",
            "Ordinary Shares Number": "15943425000.0"
        },
        "2021-09-30": {
            "Treasury Shares Number": "NaN",
            "Ordinary Shares Number": "16426786000.0"
        },
        "2020-09-30": {
            "Treasury Shares Number": "NaN",
            "Ordinary Shares Number": "16976763000.0"
        }
    }
}

有谁知道完成此任务的明智方法?

text-processing
  • 3 3 个回答
  • 127 Views

3 个回答

  • Voted
  1. Best Answer
    Stéphane Chazelas
    2023-12-19T23:46:06+08:002023-12-19T23:46:06+08:00

    我会用perl:

    $ perl -MJSON::PP -ae '
      if (/^(.*):$/) {$sheet = $1}
      elsif (/^\h+\d/) {$n = (@dates = @F)}
      elsif (/^(.*?)((?:\h+)\H+){$n}$/) {
        $i = -$n;
        $j{$sheet}->{$1} = {map {$_ => $F[$i++]} @dates}
      }
      END {print JSON::PP->new->pretty->encode(\%j)}' your-file
    {
       "Balance_sheet for AAPL" : {
          "Ordinary Shares Number" : {
             "2023-09-30" : "15550061000.0",
             "2020-09-30" : "16976763000.0",
             "2022-09-30" : "15943425000.0",
             "2021-09-30" : "16426786000.0"
          },
          "Treasury Shares Number" : {
             "2020-09-30" : "NaN",
             "2023-09-30" : "0.0",
             "2021-09-30" : "NaN",
             "2022-09-30" : "NaN"
          }
       }
    }
    

    请注意,由于 JSON 对象是 perl 关联数组的表示,因此其中成员的顺序将是随机的。canonical您可以通过设置标志 ( )来获得一致的顺序JSON::PP->new->pretty->canonical->encode(\%j),其中每个对象的成员都按键排序。

    如果 JSON 对象中的字段顺序反映表中的顺序很重要(如 中所述)perldoc JSON::PP,您可以将这些数组与不同类型的哈希绑定,以使用类似的方法来保留顺序

    $ perl -MData::Dumper -MTie::Hash::Indexed -MJSON::PP -ae '
      BEGIN{tie %j, $m = "Tie::Hash::Indexed"}
      if (/^(.*):$/) {tie my %s, $m; $j{$sheet = $1} = \%s}
      elsif (/^\h+\d/) {$n = (@dates = @F)}
      elsif (/^(.*?)((?:\h+)\H+){$n}$/) {
        tie my %s, $m;
        $i = -$n;
        %s = map {$_ => $F[$i++]} @dates;
        $j{$sheet}->{$1} = \%s
      }
      END {print JSON::PP->new->pretty->encode(\%j)}' your-file
    {
       "Balance_sheet for AAPL" : {
          "Treasury Shares Number" : {
             "2023-09-30" : "0.0",
             "2022-09-30" : "NaN",
             "2021-09-30" : "NaN",
             "2020-09-30" : "NaN"
          },
          "Ordinary Shares Number" : {
             "2023-09-30" : "15550061000.0",
             "2022-09-30" : "15943425000.0",
             "2021-09-30" : "16426786000.0",
             "2020-09-30" : "16976763000.0"
          }
       }
    }
    

    Tie::Hash::Indexed(libtie-hash-indexed-perlDebian 软件包)是提供有序哈希的几个此类模块之一。

    如果这很重要,则对于更接近您期望的格式(具有 4 个空格缩进并且 s 之后但不是之前的空格)的格式,:请替换pretty为indent->indent_length(4)->space_after( indent_length(2)for jq-style Pretty-printing)。

    • 5
  2. Ed Morton
    2023-12-20T03:44:12+08:002023-12-20T03:44:12+08:00

    使用任何 POSIX awk:

    $ cat tst.awk
    BEGIN {
        inStep = 4
        print "{"
    }
    
    sub(/:$/,"") {
        indent = inStep
        printf "%*s\"%s\": {\n", indent, "", $0
        next
    }
    
    !numDates && /^[[:space:]]/ {
        numDates = split($0,dates)
        next
    }
    
    numDates && match($0,"[[:space:]]+([^[:space:]]+[[:space:]]*){"numDates"}$") {
        indent += inStep
        printf "%s%*s\"%s\": {\n", (numItems++ ? ",\n" : ""), indent, "", substr($0,1,RSTART-1)
    
        indent += inStep
        $0 = substr($0,RSTART,RLENGTH)
        for ( i=1; i<=numDates; i++ ) {
            printf "%*s\"%s\": \"%s\"%s\n", indent, "", dates[i], $i, (i<numDates ? "," : "")
        }
        indent -= inStep
    
        printf "%*s}", indent, ""
        indent -= inStep
    }
    
    END {
        printf "\n%*s}\n", indent, ""
        print "}"
    }
    

    $ awk -f tst.awk file
    {
        "Balance_sheet for AAPL": {
            "Treasury Shares Number": {
                "2023-09-30": "0.0",
                "2022-09-30": "NaN",
                "2021-09-30": "NaN",
                "2020-09-30": "NaN"
            },
            "Ordinary Shares Number": {
                "2023-09-30": "15550061000.0",
                "2022-09-30": "15943425000.0",
                "2021-09-30": "16426786000.0",
                "2020-09-30": "16976763000.0"
            }
        }
    }
    

    如果您需要处理多个“资产负债表”块,那么只需添加以下内容:

    if ( numTables++ ) {
        printf "\n%*s},\n", indent, ""
    }
    numDates = numItems = 0
    

    紧接该sub()行下方,例如给出以下输入:

    $ cat file2
    Balance_sheet for AAPL:
    
                                                            2023-09-30      2022-09-30      2021-09-30      2020-09-30
    Treasury Shares Number                                         0.0             NaN             NaN             NaN
    Ordinary Shares Number                               15550061000.0   15943425000.0   16426786000.0   16976763000.0
    
    Balance_sheet for foo:
    
                                                            2023-09-30      2022-09-30      2021-09-30      2020-09-30
    Treasury Shares Number                                         0.0             NaN             NaN             NaN
    Ordinary Shares Number                               15550061000.0   15943425000.0   16426786000.0   16976763000.0
    

    这个脚本:

    $ cat tst.awk
    BEGIN {
        inStep = 4
        print "{"
    }
    
    sub(/:$/,"") {
        if ( numTables++ ) {
            printf "\n%*s},\n", indent, ""
        }
        numDates = numItems = 0
        indent = inStep
        printf "%*s\"%s\": {\n", indent, "", $0
        next
    }
    
    !numDates && /^[[:space:]]/ {
        numDates = split($0,dates)
        next
    }
    
    numDates && match($0,"[[:space:]]+([^[:space:]]+[[:space:]]*){"numDates"}$") {
        indent += inStep
        printf "%s%*s\"%s\": {\n", (numItems++ ? ",\n" : ""), indent, "", substr($0,1,RSTART-1)
    
        indent += inStep
        $0 = substr($0,RSTART,RLENGTH)
        for ( i=1; i<=numDates; i++ ) {
            printf "%*s\"%s\": \"%s\"%s\n", indent, "", dates[i], $i, (i<numDates ? "," : "")
        }
        indent -= inStep
    
        printf "%*s}", indent, ""
        indent -= inStep
    }
    
    END {
        printf "\n%*s}\n", indent, ""
        print "}"
    }
    

    将产生以下输出:

    $ awk -f tst.awk file2
    {
        "Balance_sheet for AAPL": {
            "Treasury Shares Number": {
                "2023-09-30": "0.0",
                "2022-09-30": "NaN",
                "2021-09-30": "NaN",
                "2020-09-30": "NaN"
            },
            "Ordinary Shares Number": {
                "2023-09-30": "15550061000.0",
                "2022-09-30": "15943425000.0",
                "2021-09-30": "16426786000.0",
                "2020-09-30": "16976763000.0"
            }
        },
        "Balance_sheet for foo": {
            "Treasury Shares Number": {
                "2023-09-30": "0.0",
                "2022-09-30": "NaN",
                "2021-09-30": "NaN",
                "2020-09-30": "NaN"
            },
            "Ordinary Shares Number": {
                "2023-09-30": "15550061000.0",
                "2022-09-30": "15943425000.0",
                "2021-09-30": "16426786000.0",
                "2020-09-30": "16976763000.0"
            }
        }
    }
    
    • 3
  3. jubilatious1
    2023-12-21T07:44:15+08:002023-12-21T07:44:15+08:00

    使用Raku(以前称为 Perl_6)

    ~$ raku -MJSON::Fast -e '
             my $a = lines[0..1].trim-trailing;         \
             my @a = slurp.map("Date" ~ *).lines.map:   \
                 *.subst(:global, / <alpha>+ % " " /, { .trans(" " => "_") } ).split(/ \s+ /);  \
             @a = [Z] @a; my %h; for 1..@a[0].elems-1 -> $j {   \
                 %h.append: @a.[0][$j] => %(@a.map( { $_.[0] => $_.[$j] } )[1..*]) };  \ 
             put to-json( $a => %h, :sorted-keys );'   file
    

    上面是用 Raku(Perl 编程语言家族的成员)编写的答案。鉴于OP发布的“资产负债表”测试文件有限,“预处理”表也相应受到限制,并且可能是定制的。因此,用于将数据整理为标准格式的前几行代码应被解释为赋予 Raku 语言的“风味”,而不是代表处理所有“资产负债表”的方法。

    1. Raku模块JSON::Fast在命令行上加载。
    2. 第一条语句将前两行读入$a标量,trim-ming 去掉尾随空格。
    3. 第二个陈述 a ). slurp将文件的剩余部分放入内存中, b )。在前面添加"Date"字符串 c )。将所有内容分解为lines, d )。每个都line被map输入,以便:e )。subst它% " "由单字符空格分隔字母(在替换部分中,空格 被trans替换为_下划线,从而纠正行标签)。然后最后f )。数据根据剩余的\s+空白进行分割,并且该表存储在@a数组中。
    4. 该@a表[Z]经过“zip”转换,使得行变成列,反之亦然(参见下面的示例)。
    5. %h声明了一个哈希值。
    6. 迭代“Shares”数据列的数量。A)。%()创建包含键值对的匿名哈希,每对都以日期(行标签)作为每个相应数据列数值的键(此处,索引删除“标签”配对)。b ). 添加另一个级别,适当的“共享”标签成为匿名哈希值的关键。转换为键/值的“共享”列被编辑为哈希值。[1..*]%()append%h
    7. 最后(此时实际上是级别c ),“Balance_Sheet”标头作为值作为散列的键$a添加回,并且转换此(现在是多级)最终散列表,添加命名参数,然后输出。%hto-json():sorted-keysput

    输入示例:

    Balance_sheet for AAPL:
    
                                                            2023-09-30      2022-09-30      2021-09-30      2020-09-30
    Treasury Shares Number                                         0.0             NaN             NaN             NaN
    Ordinary Shares Number                               15550061000.0   15943425000.0   16426786000.0   16976763000.0
    

    转换后的示例输入表[Z](减去“Balance_Sheet”标题行):

    (Date Treasury_Shares_Number Ordinary_Shares_Number)
    (2023-09-30 0.0 15550061000.0)
    (2022-09-30 NaN 15943425000.0)
    (2021-09-30 NaN 16426786000.0)
    (2020-09-30 NaN 16976763000.0)
    

    最终JSON输出:

    {
      "Balance_sheet for AAPL:": {
        "Ordinary_Shares_Number": {
          "2020-09-30": "16976763000.0",
          "2021-09-30": "16426786000.0",
          "2022-09-30": "15943425000.0",
          "2023-09-30": "15550061000.0"
        },
        "Treasury_Shares_Number": {
          "2020-09-30": "NaN",
          "2021-09-30": "NaN",
          "2022-09-30": "NaN",
          "2023-09-30": "0.0"
        }
      }
    }
    

    https://raku.land/cpan:TIMOTIMO/JSON::Fast
    https://docs.raku.org/language/hashmap
    https://docs.raku.org/
    https://raku.org

    • 1

相关问题

  • grep 从 $START 到 $END 的一组行并且在 $MIDDLE 中包含匹配项

  • 重新排列字母并比较两个单词

  • 在awk中的两行之间减去相同的列

  • 多行文件洗牌

  • 如何更改字符大小写(从小到大,反之亦然)?同时[重复]

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    模块 i915 可能缺少固件 /lib/firmware/i915/*

    • 3 个回答
  • Marko Smith

    无法获取 jessie backports 存储库

    • 4 个回答
  • Marko Smith

    如何将 GPG 私钥和公钥导出到文件

    • 4 个回答
  • Marko Smith

    我们如何运行存储在变量中的命令?

    • 5 个回答
  • Marko Smith

    如何配置 systemd-resolved 和 systemd-networkd 以使用本地 DNS 服务器来解析本地域和远程 DNS 服务器来解析远程域?

    • 3 个回答
  • Marko Smith

    dist-upgrade 后 Kali Linux 中的 apt-get update 错误 [重复]

    • 2 个回答
  • Marko Smith

    如何从 systemctl 服务日志中查看最新的 x 行

    • 5 个回答
  • Marko Smith

    Nano - 跳转到文件末尾

    • 8 个回答
  • Marko Smith

    grub 错误:你需要先加载内核

    • 4 个回答
  • Marko Smith

    如何下载软件包而不是使用 apt-get 命令安装它?

    • 7 个回答
  • Martin Hope
    user12345 无法获取 jessie backports 存储库 2019-03-27 04:39:28 +0800 CST
  • Martin Hope
    Carl 为什么大多数 systemd 示例都包含 WantedBy=multi-user.target? 2019-03-15 11:49:25 +0800 CST
  • Martin Hope
    rocky 如何将 GPG 私钥和公钥导出到文件 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Evan Carroll systemctl 状态显示:“状态:降级” 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim 我们如何运行存储在变量中的命令? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S 为什么 /dev/null 是一个文件?为什么它的功能不作为一个简单的程序来实现? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 如何从 systemctl 服务日志中查看最新的 x 行 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - 跳转到文件末尾 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla 为什么真假这么大? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis 在一个巨大的(70GB)、一行、文本文件中替换字符串 2017-12-30 06:58:33 +0800 CST

热门标签

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve