如何将行转换为列

如何将行转换为列

请帮我将行值转换为列值。我的输入file.dat是:

前两个字段分别是字母字符串和数字,输入将始终具有相同数量的字段,即6

输入

A|1|DLT|07:30|10:30|34
A|1|STG|07:30|10:30|NA
A|1|MAIN|06:30|10:30|NA
A|2|STG|07:30|10:30|NA
A|2|UNLD|04:30|10:30|90
B|1|DLT|03:30|10:30|34
B|1|STG|07:30|09:30|NA
B|1|MAIN|07:25|10:30|NA
B|1|UNLD|05:30|12:30|8

输出有变化。输出字段所有行有 18 个字段,每行将有输入文件的前两个字段,后跟按以下顺序排列的第 3 个、...、第 18 个字段,其中空白字段是输入文件中缺少的行,记录顺序为 DLT, STG,MAIN,UNLD 后面有相应的记录。

输出

A|1|DLT|07:30|10:30|34|STG|07:30|10:30|NA|MAIN|06:30|10:30|NA|||| 
A|2|||||STG|07:30|10:30|NA|||||UNLD|04:30|10:30|90
B|1|DLT|03:30|10:30|34|STG|07:30|09:30|NA|MAIN|07:25|10:30|NA|UNLD|05:30|12:30|8
  • A|1 缺少 UNLD
  • A|2 缺少 DLT 和 MAIN
  • 没有任何记录丢失

类似地,如果与输入文件的第三个字段对应的任何记录丢失,则必须将其替换为空白值,以便输出字段将具有18个领域

答案1

我的awk建议:

awk -F'|' '{ if (a[$1$2] == "") {
               a[$1$2] = $0
             } 
             else {
               a[$1$2] = a[$1$2]","$3"|"$4"|"$5"|"$6
             }
           }
           END {
             for (key in a) {
               print a[key]
             }
           }' <input.txt | sort

解释

-F'|'选项定义了字段分隔符awk用于解析行中的字段)作为字符“|”,因为这就是文件的格式。

a[...]是一个数组。数组在awk有点像 python 字典,而不是索引这实际上是字符串。对于输入文件的每一行,测试都会检查与前 2 个字段(例如第一行)if (a[$1$2] == "")对应的键(如果有条目)。$1$2 = A1如果不是(A|1|...读取第一行),则整行存储在此键 ( a[$1$2] = $0) 中。如果已经有内容(A|1|...已经存储了另一行),那么我们用逗号连接该条目,并用“|”分隔从 3 到 6 的字段。 ( a[$1$2] = a[$1$2]","$3"|"$4"|"$5"|"$6)。

最后,当我们浏览完文件后,我们需要输出每个键的条目。我们在一个END块中执行此操作(一旦读取了所有文件,就会执行这些指令)。为此,我们只需遍历数组 ( for (key in a)) 的所有键并打印每个键的条目即可。

然后,最终输出通过管道传输到sort,因为awk不会按字母数字顺序遍历数组键,因此对输出进行排序会更干净,以便您得到A|1|...后面的行,A|2|...依此类推。


你的最后一次编辑让整个事情变得有点棘手。所需的说明awk会变得有点复杂,所以我建议您创建一个脚本文件(创建一个扩展名为 例如 的awk文本文件)。将以下脚本复制到其中:.awkmyScript.awk

BEGIN { FS="|" }

$3 == "DLT" {
  dlt[$1"|"$2]=$3"|"$4"|"$5"|"$6
  a[$1"|"$2]++
}

$3 == "STG" {
  stg[$1"|"$2]=$3"|"$4"|"$5"|"$6
  a[$1"|"$2]++
}

$3 == "MAIN" {
  main[$1"|"$2]=$3"|"$4"|"$5"|"$6
  a[$1"|"$2]++
}

$3 == "UNLD" {
  unld[$1"|"$2]=$3"|"$4"|"$5"|"$6
  a[$1"|"$2]++
}

END {
  for (key in a) {
    if (dlt[key] == "") dlt[key]="|||"
    if (stg[key] == "") stg[key]="|||"
    if (main[key] == "") main[key]="|||"
    if (unld[key] == "") unld[key]="|||"
    print key"|"dlt[key]"|"stg[key]"|"main[key]"|"unld[key]          
  }
}

使用它:

awk -f myScript.awk <input.txt | sort

如果你理解了我最初答案的解释,你应该能够理解这个算法。这次我们为每种数据类型(dlt、stg、main 和 unld)创建一个数组,并将它们的值存储在与前两个​​字段对应的键处。数组“ a”用于跟踪所有可能的键。最后我们遍历 array 的键a,如果其中一个数据数组在该键处为空,则填充“|||”如你所愿,这样每行都有 18 个字段。

答案2

略有不同瓦伦丁·B.的回答

awk -F"|" '
     {
        key = $1 "|" $2
        a[key] = 1
        b[key][$3] = $3 "|" $4 "|" $5 "|" $6
     }
 END {
        subtype[1] = "DLT"
        subtype[2] = "STG"
        subtype[3] = "MAIN"
        subtype[4] = "UNLD"
        for (key in a)
            {
                output = key
                for (i = 1; i <= 4; i++)
                    {
                        st = subtype[i]
                        if (b[key][st] == "")
                                output = output "||||"
                        else
                                output = output "|" b[key][st]
                    }
                print output
            }
     }' file.dat

如在瓦伦丁·B.的回答

  • -F"|"将字段分隔符设置为|,以便我们可以从输入行中提取字段。
  • key设置为A|1A|2、 或B|1 (或者通常是前两个字段的串联)。我们使用它来组合与该组合键相关的(最多)四行数据。
  • 设置a[key]为 1 以记录我们看到的数据(键)。
  • 设置b[key][$3]为键后该行的其余部分。例如,b["A|1"]["DLT"]将被设置为"DLT|07:30|10:30|34".这样,我们就可以记录全部我们看到的数据(假设行不超过六个字段,如问题中指定)。请注意,我们在不知道 中可能包含什么的情况下执行此操作$3

当我们读取完所有数据后:

  • 定义子类型的数组(即有效值$3,“DLT”、“STG”、“MAIN”和“UNLD”),因此我们不需要多次列出它们。
  • 迭代我们见过的所有键。开始构建输出线。
  • 迭代四个子类型(上面列出)。如果我们有该键和该子类型的数据,请将其附加到输出行;否则,附加四个空白字段。
  • 打印该行。

sort如果您希望对输出进行排序,请将其通过管道传入。 (我说“如果”是因为,虽然问题节目示例输出已排序,但并不表示输出必须已排序。)

这个问题对于第二个字段中的内容并不精确;它只说它们是“数字”。如果它们可能是具有不同位数的整数,并且您希望它们按数字排序,请使用

sort -t"|" -k1,1 -k2,2n

答案3

捕获第四个字段中的值并将其存储在名为“val”的变量中

创建一个数组并根据第一个和第二个字段附加数组的值。

最后,打印数组值并进行小的调整(替换、删除等)

$ awk -F\| '{ val=$3;
              for (i=4;i<=NF;i++) {val=val"|"$i}
              Arr[$1OFS$2]=Arr[$1OFS$2]","val;
              next
            }
        END { for (i in Arr) {
                  A=Arr[i];
                  sub(" ","|",i);
                  print i,A
              }
            }' test.txt | sed "s/ ,/\|/"
A|1|DLT|07:30|10:30|34 ,STG|07:30|10:30|NA ,MAIN|06:30|10:30|NA
B|1|DLT|03:30|10:30|34 ,STG|07:30|09:30|NA ,MAIN|07:25|10:30|NA ,UNLD|05:30|12:30|8
A|2|UNLD|04:30|10:30|90 ,DLT|08:30|11:30|4 ,STG|07:30|10:30|NA

相关内容