【问题标题】:Convert rows into columns for semi structured data将行转换为半结构化数据的列
【发布时间】:2017-01-27 19:52:00
【问题描述】:

我有一个看起来像这样的数据集

<SUBBEGIN
    IMSI=xxxxxxxxxxxx;
    MSISDN=xxxxxxxxx;
    DEFCALL=TS11;
    CURRENTNAM=BOTH;
    CAT=COMMON;
    VOLTE_TAG=NOT_DEFINED;
    HLR_INDEX=1;
    PS_MSISDNLESS_SUPPORTED=FALSE;
    CS_MSISDNLESS_SUPPORTED=FALSE;
    CSRATTYPE=NO-NO-NO-NO-NO;
    PSRATTYPE=NO-NO-NO-NO-NO;
    ICI=NO;
    STE=NO;
<SUBEND

<SUBBEGIN
    IMSI=xxxxxxxxxxxx;
    MSISDN=xxxxxxxxx;
    DEFCALL=TS11;
    CURRENTNAM=BOTH;
    VOLTE_TAG=NOT_DEFINED;
    HLR_INDEX=1;
    PS_MSISDNLESS_SUPPORTED=FALSE;
    CS_MSISDNLESS_SUPPORTED=FALSE;
    CSRATTYPE=NO-NO-NO-NO-NO;
<SUBEND

这本质上是一条记录,后面是相同格式的多行。我希望输出格式为:

IMSI|MSISDN|DEFCALL|CURRENTNAM|CAT...
xxxx|xxxx|TS11|BOTH|COMMON|COMMON

非常感谢任何帮助。

【问题讨论】:

  • 这可能会有所帮助:stackoverflow.com/questions/29108949/…
  • @sp asic 感谢您的提示,我只能使用cat file | sed -r 's/[[:alnum:]]+=/\n&amp;/g' | awk -F= '$1=="IMSI"{print $2}' 解析文件中的一个变量,例如 IMSI。我怎样才能让代码适用于所有其他变量
  • 你可以使用 Python 或 Perl 吗?
  • 当您的输入可以包含多条记录时,最好在示例输入/输出中显示至少 2 条记录,否则您将获得适用于 1 条记录但不超过 1 条记录的解决方案,因为您仅提供 1 来证明您的问题并供我们测试。您目前有多个答案不适用于 2 个或更多输入记录,而答案可能更简单,具体取决于记录之间是否有空行。

标签: unix text awk sed grep


【解决方案1】:

使用 tr 的解决方法

tr -s '\n' ',' < file > tmpfile;

这给了我

形式的输出
<SUBBEGIN    IMSI=xxxxxxxxxxxx;    MSISDN=xxxxxxxxx;    DEFCALL=TS11;    CURRENTNAM=BOTH;    CAT=COMMON;    VOLTE_TAG=NOT_DEFINED;    HLR_INDEX=1;    PS_MSISDNLESS_SUPPORTED=FALSE;    CS_MSISDNLESS_SUPPORTED=FALSE;    CSRATTYPE=NO-NO-NO-NO-NO;    PSRATTYPE=NO-NO-NO-NO-NO;    ICI=NO; STE=NO; <SUBEND

替换字符串"&lt;SUBBEGIN" with \n

【讨论】:

    【解决方案2】:

    从这个开始:

    sed '/<SUBBEGIN/{:a;N;/\<SUBEND/!ba;s/\n[^=]*=/ /g;s/.*SUBBEGIN//;s/;/|/g}' input
    

    【讨论】:

      【解决方案3】:
      $ cat tst.awk
      BEGIN {FS="[=;]"; OFS="|" }
      /^<SUB/ {
          if (/END/) {
              print (hdrPrinted++ ? "" : hdr ORS ) rec
              hdr = rec = ""
          }
          next
      }
      {
          sub(/^[[:space:]]+/,"")
          hdr = (hdr=="" ? "" : hdr OFS) $1
          rec = (rec=="" ? "" : rec OFS) $2
      }
      
      $ awk -f tst.awk file
      IMSI|MSISDN|DEFCALL|CURRENTNAM|CAT|VOLTE_TAG|HLR_INDEX|PS_MSISDNLESS_SUPPORTED|CS_MSISDNLESS_SUPPORTED|CSRATTYPE|PSRATTYPE|ICI|STE
      xxxxxxxxxxxx|xxxxxxxxx|TS11|BOTH|COMMON|NOT_DEFINED|1|FALSE|FALSE|NO-NO-NO-NO-NO|NO-NO-NO-NO-NO|NO|NO
      

      【讨论】:

        【解决方案4】:
        $ cat test.txt
        /<SUBBEGIN/ {f=1; next}      # at start flag up
        /<SUBEND/ {                  # at end 
            print b ORS c            # print
            f=0; b=c=""              # flag up and reset variables
        } 
        f {                          # between markers
            split($1,a,"[=;]")       # gather to 2 variables
            b=b a[1] "|"
            c=c a[2] "|"
        }
        

        测试一下:

        $ awk -f test.awk test.txt
        IMSI|MSISDN|DEFCALL|CURRENTNAM|CAT|VOLTE_TAG|HLR_INDEX|PS_MSISDNLESS_SUPPORTED|CS_MSISDNLESS_SUPPORTED|CSRATTYPE|PSRATTYPE|ICI|STE|
        xxxxxxxxxxxx|xxxxxxxxx|TS11|BOTH|COMMON|NOT_DEFINED|1|FALSE|FALSE|NO-NO-NO-NO-NO|NO-NO-NO-NO-NO|NO|NO|
        

        【讨论】:

        • 这会在每行的末尾添加一个额外的|。话虽如此,OP 并没有向我们展示他问题中每一行的结尾,所以谁知道呢,也许这就是我们想要的。它还将在输出中多次打印标题行,而不是在开始时打印一次,但再次因为 OP 没有向我们展示多个输入/输出块......
        • 嘿,那是我的借口! :D
        【解决方案5】:

        为简单起见使用不同的输入文件

        $ cat ip.txt 
        <SUBBEGIN
            i1=abc;
            i2=ijk;
            i3=xyz;
            k1=NO;
            t1=YES;
        <SUBEND
        
        <SUBBEGIN
            i1=foo;
            i2=bar;
            i3=test;
            k1=YES;
            t1=NO;
        <SUBEND
        
        $ perl -nle '
        $s=/<SUBBEGIN/ if /<SUB/;
        if($s && !/<SUB/)
        {
            ($k,$v) = /\S+(?==)|=\K[^;]+/g;
            push(@key, $k);
            push(@val, $v);
        }
        elsif(@key)
        {
            print join "|", @key;
            print join "|", @val;
            @key = ();
            @val = ();
        }
        ' ip.txt
        i1|i2|i3|k1|t1
        abc|ijk|xyz|NO|YES
        i1|i2|i3|k1|t1
        foo|bar|test|YES|NO
        
        • $s 如果输入行包含 &lt;SUBBEGIN,则设置标志
        • 如果设置了标志并且输入行不包含&lt;SUB
          • 提取键、值对
          • 将它们填充到两个不同的数组中
        • 一旦输入行再次包含&lt;SUB
          • 检查数组之一(比如@key)是否不为空
          • |为分隔符打印键值数组
          • 清空数组
        • 无论数据结构之间是否存在空行,这都会起作用

        【讨论】:

          【解决方案6】:

          这是另一种解决方案:

          awk 脚本

          #!/bin/awk
          
          function print_record( hdr )
          {
              str = ""
          
              for( i = 1; i <= 13; i++ )
              {
                  if( hdr )
                  {
                      value = substr( $i, 1, index( $i, "=" ) - 1 )
                  }
                  else
                  {
                      value = substr( $i, index( $i, "=" ) + 1 )
                  }
          
                  gsub( /^[ \t]+/, "", value )
          
                  if( length(str) > 0 )
                      str = str OFS
          
                  str = str value
              }
          
              print str
          }
          
          BEGIN {
              RS="<SUBBEGIN\n"
              FS=";\n"
              hdr=1
              OFS="|"
          }
          
          {
              if( index( $0, "=" ) && index( $0, ";" ) )
              {
                  if( hdr )
                  {
                      print_record( 1 )
                      hdr = 0;
                  }
          
                  print_record( 0 )
              }
          }
          
          # eof #
          

          输入文件

          <SUBBEGIN
              IMSI=xxxxxxxxxxxx;
              MSISDN=xxxxxxxxx;
              DEFCALL=TS11;
              CURRENTNAM=BOTH;
              CAT=COMMON;
              VOLTE_TAG=NOT_DEFINED;
              HLR_INDEX=1;
              PS_MSISDNLESS_SUPPORTED=FALSE;
              CS_MSISDNLESS_SUPPORTED=FALSE;
              CSRATTYPE=NO-NO-NO-NO-NO;
              PSRATTYPE=NO-NO-NO-NO-NO;
              ICI=NO;
              STE=NO;
          <SUBEND
          <SUBBEGIN
              IMSI=yyyyyyyyyy;
              MSISDN=yyyyyyyyy;
              DEFCALL=TS11;
              CURRENTNAM=BOTH;
              CAT=COMMON;
              VOLTE_TAG=NOT_DEFINED;
              HLR_INDEX=2;
              PS_MSISDNLESS_SUPPORTED=TRUE;
              CS_MSISDNLESS_SUPPORTED=FALSE;
              CSRATTYPE=NO-YES-NO-NO-NO;
              PSRATTYPE=NO-NO-NO-YES-NO;
              ICI=NO;
              STE=NO;
          <SUBEND
          <SUBBEGIN
              IMSI=zzzzzzzzzz;
              MSISDN=zzzzzzzzzzzzzzz;
              DEFCALL=TS11;
              CURRENTNAM=BOTH;
              CAT=COMMON;
              VOLTE_TAG=NOT_DEFINED;
              HLR_INDEX=3;
              PS_MSISDNLESS_SUPPORTED=FALSE;
              CS_MSISDNLESS_SUPPORTED=TRUE;
              CSRATTYPE=NO-YES-YES-NO-NO;
              PSRATTYPE=NO-NO-YES-YES-NO;
              ICI=YES;
              STE=YES;
          <SUBEND
          

          输出

          $ awk -f script.awk -- input.txt
          IMSI|MSISDN|DEFCALL|CURRENTNAM|CAT|VOLTE_TAG|HLR_INDEX|PS_MSISDNLESS_SUPPORTED|CS_MSISDNLESS_SUPPORTED|CSRATTYPE|PSRATTYPE|ICI|STE
          xxxxxxxxxxxx|xxxxxxxxx|TS11|BOTH|COMMON|NOT_DEFINED|1|FALSE|FALSE|NO-NO-NO-NO-NO|NO-NO-NO-NO-NO|NO|NO
          yyyyyyyyyy|yyyyyyyyy|TS11|BOTH|COMMON|NOT_DEFINED|2|TRUE|FALSE|NO-YES-NO-NO-NO|NO-NO-NO-YES-NO|NO|NO
          zzzzzzzzzz|zzzzzzzzzzzzzzz|TS11|BOTH|COMMON|NOT_DEFINED|3|FALSE|TRUE|NO-YES-YES-NO-NO|NO-NO-YES-YES-NO|YES|YES
          

          希望对你有帮助!

          【讨论】:

          • 由于多字符 RS,您应该提到它是 gawk 特有的。不要将字段数硬编码为13,因为您可以轻松地从输入中计算它们。
          【解决方案7】:

          使用 unix 工具链,也许是最短的...

          $  sed '/^</d' file | tr '=' '\n' | tr -d ' ;' | pr -13ts'|'
          
          IMSI|MSISDN|DEFCALL|CURRENTNAM|CAT|VOLTE_TAG|HLR_INDEX|PS_MSISDNLESS_SUPPORTED|CS_MSISDNLESS_SUPPORTED|CSRATTYPE|PSRATTYPE|ICI|STE
          xxxxxxxxxxxx|xxxxxxxxx|TS11|BOTH|COMMON|NOT_DEFINED|1|FALSE|FALSE|NO-NO-NO-NO-NO|NO-NO-NO-NO-NO|NO|NO
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2017-11-26
            • 1970-01-01
            • 1970-01-01
            • 2021-10-05
            相关资源
            最近更新 更多