从可变长度换行输入创建一个csv

时间:2013-12-16 11:36:05

标签: bash awk

看起来像一个简单的,但我已经玩了一会儿,找不到优雅的东西!

所以我有这样的数据:

Field1 09:30
Field2 H
Field3 Happy
Field1 09:35
Field3 Sad
Field1 09:40
Field2 C
Field1 09:45
Field2 P
Field3 Pleased

...基本上Field1将始终存在,其他字段是可选的。我想把它剥离成一个csv(遗憾的是我不能使用python)以便适当地留下空格

09:30, H, Happy
09:35, , Sad
09:40, C, 
09:45, P, Pleased 

5 个答案:

答案 0 :(得分:4)

让我们试试这个:

awk 'BEGIN{OFS=", "}
     p && /Field1/
         {    print a["Field1"], a["Field2"], a["Field3"];
              a["Field1"]=a["Field2"]=a["Field3"]=""
         }
     {a[$1]=$2; p=1}
     END{print a["Field1"], a["Field2"], a["Field3"]}
    ' file

它返回:

$ awk 'BEGIN{OFS=", "} p && /Field1/ {print a["Field1"], a["Field2"], a["Field3"]; a["Field1"]=a["Field2"]=a["Field3"]=""} {a[$1]=$2; p=1} END{print a["Field1"], a["Field2"], a["Field3"]}' file
09:30, H, Happy
09:35, , Sad
09:40, C, 
09:45, P, Pleased

解释

  • BEGIN{OFS=", "}将输出字段分隔符设置为,(逗号,空格)。
  • p && /Field1/ {}如果p标记为“on”且该行包含Field1,请执行{}
  • print a["Field1"], a["Field2"], a["Field3"];打印a[]数组的三个值。
  • a["Field1"]=a["Field2"]=a["Field3"]=""清空数组。
  • 每行
  • {a[$1]=$2; p=1},将第二列值存储在a[]数组中。此外,激活p标记,以便在找到下一个Field1时开始打印行。
  • END{print a["Field1"], a["Field2"], a["Field3"]}打印最后一段数据。

答案 1 :(得分:3)

以下是perl中的解决方案:

perl -lane 'if(/Field([\d])/){
               if($1==1 && $.!=1)
                 {
                  print join ",",@a;
                  undef @a 
                 }
                 $a[$1-1]=$F[1]}
             END{print join ",",@a}' your_file

/Field([\d])/ - [\ d]周围的大括号将捕获$ 1中的数字,以后可以用作数组索引。

$a[$1-1]=$F[1]->将先前捕获的索引中的第二个字段存储在数组中。

if($1==1 && $.!=1)
                 {
                  print join ",",@a;
                  undef @a 
                 }# 

一旦捕获的小数为1就打印数组并清空数组。

将在最后留下一个数组,这些数组将打印在END块中。

下面测试:

> cat temp
Field1 09:30
Field2 H
Field3 Happy
Field1 09:35
Field3 Sad
Field1 09:40
Field2 C
Field1 09:45
Field2 P
Field3 Pleased
> perl -lane 'if(/Field([\d])/){if($1==1 && $.!=1){print join ",",@a;undef @a }$a[$1-1]=$F[1]}END{print join ",",@a}' temp
09:30,H,Happy
09:35,,Sad
09:40,C
09:45,P,Pleased
>

答案 2 :(得分:1)

以下是awk的替代方案:

# new records always starting with Field1
/Field1/ {
  # print record if it isset
  if(length(r[0])>0) {
    printf "%s, %s, %s\n", r[0], r[1], r[2]
  };  

  # reinitialize record
  r[0]=r[1]=r[2]=""

  # copy value
  r[0]=$2
}

/Field2/ {
  # copy value
  r[1]=$2
}

/Field3/ {
  # copy value
  r[2]=$2
}

# the END block idea comes from @fedorqui. Thanks!
END {
  # print record if it isset
  if(length(r[0])>0) {
    printf "%s, %s, %s\n", r[0], r[1], r[2]
  };  
}

将脚本保存在csv.awk中。然后像这样执行awk

awk -f csv.awk input.txt

答案 3 :(得分:0)

似乎你厌倦了像我一样的数据格式化工作。
下面是我使用了很多,略有修改的片段。您可以在第2行中定义任意数量的字段(不一定是相同的模式),然后发出awk -f so.awk input.txt(假设您将此awk片段保存在so.awk中,输入文件为input.txt)。

享受:)

EGIN {
    FIELD_LIST = "Field1,Field2,Field3"; # fields definition, in order
    split(FIELD_LIST, fields, ",");
}

{
    pos = find(fields, $1);
    if (pos == 0) {
        print("unexpected field " $1);
        exit 1;
    }
    if ($1 == fields[1] && NR>1) { # record start
        echo_record(fields, rec);
        split("", rec)
    }
    rec[$1] = $2; # store field value
}
END {
    echo_record(fields, rec);
}

# find position of val in arr. 0 if not found
function find(arr, val)
{
    i=1;
    for (idx in arr) {
        if (arr[idx] == val) return i;
        ++i;
    }
    return 0;
}

function echo_record(fileds, record)
{
    start = 0
    for (key in fields) {
        if (start) printf(", ");
        start = 1;
        val = record[fields[key]];
        if (val) printf("%s", val);
        else printf(" ");
    }
    printf("\n");
}

答案 4 :(得分:0)

再一次。

function dump() { if (a[1]!="") print a[1],a[2],a[3]; a[2]=a[3]=""; }; 
BEGIN { RS="Field"; OFS=", "; a[1]=a[2]=a[3]="" } 
END {dump()}
{ if ($1=="1") dump(); a[$1]=$2; }