CSV2 dat文件头和列操作

时间:2014-04-22 14:03:46

标签: bash awk

我有一个CSV文件,转换为.DAT。我有一个AWK文件,它假设要进行DAT文件的映射。 AWK文件中的代码如下所示。

DAT文件的内容如下所示(制表符分隔):

ODT AGE CDT CO SEX TIME VALUE COMMENT
P3 Y6-8 ACT FG F 2011 1297
P4 Y3-4 EMP FG M 2011 6940 b
P1 Y7-9 GRT FG F 2011 0 c

我需要做的是:

  1. 将标题与列顺序保持在一起 - 完成
  2. 如果COMMENT的值为" c"如果COMMENT的值是" b"那么必须将列COMMENT更改为STRING_COM。 (或其他任何不同于" c")COMMENT列必须更改为STRING_STATUS
  3. 除了此处提到的栏目外,无论收到哪些栏目,都必须将其删除
  4. VALUE列应重命名为" NUMB"
  5. 这是我的代码,只修复了第一点

    BEGIN {
      FS = "," ;
      OFS = " " ;
    }
    
    {
      if(NR == 1)
      {
        split($0, tmp, ",");
        for(i = 1; i <= NF; i++)
        fields[tmp[i]] = i
        print tmp[1], tmp[2], tmp[3], tmp[4], tmp[5], tmp[6], tmp[7], tmp[8]
    
       else
       {
         split($0, tmp, ",");
         for(i = 1; i <= NF; i++)
         fields[tmp[i]] = i
    
        print tmp[1], tmp[2], tmp[3], tmp[4], tmp[5], tmp[6], tmp[7], tmp[8]
      }
    }
    

    dat文件中的预期结果:

    ODT AGE CDT CO SEX TIME NUMB STRING_COM STRING_STATUS
    P3 Y6-8 ACT FG F 2011 1297
    P4 Y3-4 ERT FG M 2011 6940 b
    P1 Y7-9 GRT FG F 2011 0 c
    

    在CSV文件中,分隔符为&#34;,&#34;但是dat文件必须带有制表符分隔符,这就是为什么我有代码.... FS =&#34;,&#34; 关于第三点,解释是: 我可能会收到其他不需要的列。 总而言之,我已经以某种方式指定只需要这些列:ODT AGE CDT CO SEX TIME VALUE COMMENT) 收到的任何其他列都必须被忽略

    CSV文件为:

    ODT,AGE,CDT,CO,SEX,TIME,VALUE,COMMENT
    P3,Y6-8,ACT,FG,F,2011,1297,
    P4,Y3-3,EMP,FG,M,2011,6940,b
    P1,Y7-9,GRT,FG,F,2011,0,c
    

    下面提供的解决方案工作正常。 还有一些问题:

    如果我在AGE或CDT之后将列COMMENT放在&#34; c&#34;字符在第二行添加。第二行包含两个选项卡,然后是&#34; c&#34;字符。 如果COMMENT在最后并包含字符&#34; c&#34;,&#34; bcc&#34;结果仅在STRING_STATUS上进行,而不是进行分离&gt; &#34; C&#34;转到STRING_COM和&#34; bcc&#34;转到STRING_STATUS

    必须实施以下案例:

    1. 如果VALUE是&#34;:&#34;然后NUMB为空
    2. 如果VALUE是&#34;:&#34;和评论&#34; c&#34;然后NUMB为空,STRING_COM为&#34; c&#34;
    3. 如果VALUE是&#34;:&#34;和评论&#34; u&#34;然后NUMB为空,STRING_STATUS为&#34; u&#34;
    4. 如果VALUE是&#34; 14,38&#34;和评论&#34; d&#34;然后NUMB是&#34; 1438&#34;和STRING(两者)为空
    5. 如果VALUE是&#34; 14,38&#34;和评论&#34; du&#34;然后NUMB是&#34; 1438&#34;和STRING_STATUS是&#34; u&#34;
    6. 如果VALUE是&#34;:&#34;和评论&#34; cd&#34;然后NUMB为空,STRING_COM为&#34; c&#34;
    7. 如果VALUE是&#34;:&#34;和评论&#34; bc&#34;然后NUMB为空,STRING_COM为&#34; c&#34;和STRING_STATUS是&#34; b&#34;
    8. 我该怎么做?

      是否有人可以帮助解决这个问题?

2 个答案:

答案 0 :(得分:0)

如果所有行都具有相同的列顺序:

awk '
BEGIN {
  FS=","; OFS="\t";
  a["ODT"]=1;a["AGE"]=1;a["CDT"]=1;a["CO"]=1;
    a["SEX"]=1;a["TIME"]=1;a["VALUE"]=1;a["COMMENT"]=1;
}
NR==1 {
    $NF=substr($NF,1,length($NF)-1);
    for(i=1;i<=NF;i++) if($i in a) a[$i]=i;
}
{   print $a["ODT"],$a["AGE"],$a["CDT"],$a["CO"],$a["SEX"],$a["TIME"],NR==1?"NUMB":$a["VALUE"],
    NR==1?"STRING_COM"OFS"STRING_STATUS":($a["COMMENT"]!="c"?""OFS$a["COMMENT"]:$a["COMMENT"]);
}' input.txt

我稍微更改了输入,添加了一列(ADDED)并重新排序了另外两列(TIMEVALUE):

ODT,ADDED,AGE,CDT,CO,SEX,VALUE,TIME,COMMENT
P3,111,Y6-8,ACT,FG,F,1297,2011,
P4,222,Y3-3,EMP,FG,M,6940,2011,b
P1,333,Y7-9,GRT,FG,F,0,2011,c

输出:

ODT AGE CDT CO  SEX TIME    NUMB    STRING_COM  STRING_STATUS
P3  Y6-8    ACT FG  F   2011    1297        
P4  Y3-3    EMP FG  M   2011    6940        b
P1  Y7-9    GRT FG  F   2011    0   c

修改

如果输入的列多于所需的列,则额外的列不会出现在输出中,例如我在此处使用的输入有一个额外的列ADDED,它不会出现在输出中。

它将按照所讨论的首选顺序打印输出,即ODT AGE CDT CO SEX TIME NUMB STRING_COM STRING_STATUS,无论它们在输入中出现的顺序如何。再次在输入中,我使用VALUE之前出现的列TIME,但在输出TIME中是第一列而NUMB是第二列。通过

  

如果所有行都具有相同的列顺序

我的意思是如果所有行都遵循标题行的顺序。如果标题中的顺序为ODT AGE CDT CO SEX TIME VALUE COMMENT,则如果标题中的顺序为AGE ODT CDT CO SEX TIME VALUE COMMENT,则所有行中的数据将按此顺序显示,则所有其他行中的数据都具有此顺序。它并不假设例如ODT始终是所有文件中的第一列,它假定在文件中订单由标题定义。如果不是这样,那么代码就不会起作用。

<强> EDIT2

我测试过它:

ODT,ADDED,AGE,CDT,CO,SEX,VALUE,TIME,COMMENT
P3,111,Y6-8,ACT,FG,F,1297,2011,cd
P4,222,Y3-3,EMP,FG,M,6940,2011,bd
P1,333,Y7-9,GRT,FG,F,0,2011,c

作为输入文件,输出为:

ODT AGE CDT CO  SEX TIME    NUMB    STRING_COM  STRING_STATUS
P3  Y6-8    ACT FG  F   2011    1297        cd
P4  Y3-3    EMP FG  M   2011    6940        bd
P1  Y7-9    GRT FG  F   2011    0   c

视觉bdcd位于STRING_COM列下,原因是标题长度超过标签大小,通常为8个字符。输出是制表符分隔的,可以打印两个选项卡以使它们显示在STRING_STATUS下,但如果输出打算由另一个程序使用,则可能会导致一些问题,因为它会产生一个空字段。如果您只需要打印,可以将第12行更改为:

NR==1?"STRING_COM"OFS"STRING_STATUS":($a["COMMENT"]!="c"?""OFS""OFS$a["COMMENT"]:$a["COMMENT"]);

输出将是:

ODT AGE CDT CO  SEX TIME    NUMB    STRING_COM  STRING_STATUS
P3  Y6-8    ACT FG  F   2011    1297            cd
P4  Y3-3    EMP FG  M   2011    6940            bd
P1  Y7-9    GRT FG  F   2011    0   c

答案 1 :(得分:0)

我用Ashkan的答案作为起点,并提出了下面的脚本。但是,这两点在CSV文件的上下文中没有意义,除非VALUE被引号(或其他东西)包围。请澄清。

  
      
  1. 如果VALUE为“14,38”而COMMENT为“d”,那么NUMB为“1438”且STRING(两者)为空
  2.   
  3. 如果VALUE为“14,38”且COMMENT为“du”,则NUMB为“1438”且STRING_STATUS为“u”
  4.   

Awk脚本:

BEGIN {
    FS=","; OFS="\t";
    a["ODT"]=1;a["AGE"]=1;a["CDT"]=1;a["CO"]=1;
    a["SEX"]=1;a["TIME"]=1;a["VALUE"]=1;a["COMMENT"]=1;
}
NR==1 {
    for (i=1;i<=NF;i++) {
        if ($i in a) a[$i]=i;
    }
    print "ODT","AGE","CDT","CO","SEX","TIME","NUMB","STRING_COM","STRING_STATUS"
}
NR!=1{
    split($0,line,/,/);
    value = line[a["VALUE"]];
    comment = line[a["COMMENT"]];
    string_com="";
    string_status="";
    if (value==":") {
        value="";
        if (comment=="c") string_com = "c";
        if (comment=="u") string_status = "u";
        if (comment=="cd") string_com = "c";
        if (comment=="bc") {
            string_com = "c";
            string_status = "b";
        }
    }
    else {
        if (comment=="c")
            string_com = "c";
        else if (comment=="d") {
            # convert value
            string_com = "";
            string_status = "";
        }
        else if (comment=="du") {
            # convert value
            string_status = "";
        }
        else
            string_status = comment;
    }
    print line[a["ODT"]],
        line[a["AGE"]],
        line[a["CDT"]],
        line[a["CO"]],
        line[a["SEX"]],
        line[a["TIME"]],
        value,
        string_com,
        string_status
}