![解析具有可变列数的管道分隔文件并替换缺失的列以转换为固定列数](https://linux22.com/image/178916/%E8%A7%A3%E6%9E%90%E5%85%B7%E6%9C%89%E5%8F%AF%E5%8F%98%E5%88%97%E6%95%B0%E7%9A%84%E7%AE%A1%E9%81%93%E5%88%86%E9%9A%94%E6%96%87%E4%BB%B6%E5%B9%B6%E6%9B%BF%E6%8D%A2%E7%BC%BA%E5%A4%B1%E7%9A%84%E5%88%97%E4%BB%A5%E8%BD%AC%E6%8D%A2%E4%B8%BA%E5%9B%BA%E5%AE%9A%E5%88%97%E6%95%B0.png)
我有一个用管道分隔的 ( |
) 文件,其中每行可以有可变数量的列/字段。只有前两个字段和最后两个字段始终存在,中间可以有变化(但偶数)数量的字段,最多 10 个,因此该行总共最多有 14 个字段 (2 + 0 ... 10 + 2).
目标是替换“缺失”字段并将文件转换为每行具有固定列数的文件。
C_A
“变量”字段的特点是始终由一个...形式的“索引键”C_E
和一个值组成。
输入示例:
10|100|C_A|val_18|C_D|val_20|50|60
40|200|C_A|val_5|C_B|val_10|C_C|val_30|C_D|val_90|C_E|val_83|40|45
80|100|C_E|val_90|50|60
这是预期的输出:
10|100|C_A|val_18|||||C_D|val_20|||50|60
40|200|C_A|val_5|C_B|val_10|C_C|val_30|C_D|val_90|C_E|val_83|40|45
80|100|||||||||C_E|val_90|50|60
答案1
GNU awk
我们可以通过将名称与字段索引映射来解决这个问题。
- C_A => $3
- C_B => $5 ...等等
$ awk '
BEGIN {
OFS = FS = "|"
n = split("A-B-C-D-E", x, "-")
for (i=1; i<=n; ++i) h["C_" x[i]] = 2*(i-1) + 3
}
{
# record which out of
# ca/cb/.../ce present
for (i=3; i<NF-2; i+=2) seen[$i] = $(i+1)
# store fields in preparation
# for re-filling them based on seen
nf = split($0, f, FS); $0=""
# fill up the first two..easy does it
$(1) = f[1]
$(2) = f[2]
# recall which fields c_?
# were seen then fill up
# corresponding field and field value
for (var in h) {
i = h[var]
if (var in seen) {
$(i) = var
$(i+1) = seen[var]
} else { $(i) = $(i+1) = ""}
}
# append the last two fields
$(NF+1) = f[nf-1]
$(NF+1) = f[nf]
# above line **NOT** a typo
# clear out the array seen
# for the next iteration
split("", seen, ".")
}1
' file
结果:
10|100|C_A|val_18|||||C_D|val_20|||50|60
40|200|C_A|val_5|C_B|val_10|C_C|val_30|C_D|val_90|C_E|val_83|40|45
80|100|||||||||C_E|val_90|50|60
答案2
将变量字段读入数组,打印数组,当不在一行中使用时生成空字符串(不要忘记-F '|'
):
BEGIN{
# initialize expected keys for file (value 1 not used)
all_keys["C_A"]=1;
all_keys["C_B"]=1;
all_keys["C_C"]=1;
all_keys["C_D"]=1;
all_keys["C_E"]=1;
}
{
# fill arrays for line
for(i=3;i<NF-1;i+=2) {
key[$i]=$i;
value[$i]=$i+1;
}
# first two fields
printf $1"|"$2"|";
# all variable key/value pairs, occurring on line or not
for (k in all_keys) {
printf key[k]"|"value[k]"|";
}
# last two fields
print $NF-1"|"$NF;
# delete arrays so we don't carry over values into the next line
delete key;
delete value;
}