2

在文本文件中,我在一列中有一系列数字,前面有一个短字符串。它是“NAME”下示例文件中的第 5 列:

SESSION NAME:   session
SAMPLE RATE:    48000.000000
BIT DEPTH:  16-bit
SESSION START TIMECODE: 00:00:00:00.00
TIMECODE FORMAT:    24 Frame
# OF AUDIO TRACKS:  2
# OF AUDIO CLIPS:   2
# OF AUDIO FILES:   2


M A R K E R S  L I S T I N G
#       LOCATION        TIME REFERENCE      UNITS       NAME                                COMMENTS
2       0:00.500        24000               Samples     xxxx0001                            
3       0:03.541        170000              Samples     xxxx0002                            
4       0:05.863        281458              Samples     xxxx0003                            
5       0:08.925        428430              Samples     xxxx0004                            
6       0:10.604        509025              Samples     xxxx0005                            
7       0:13.973        670742              Samples     xxxx0006                            
8       0:15.592        748453              Samples     xxxx0008                            
9       0:19.243        923666              Samples     xxxx0008


在上面的示例中,缺少 0007,而重复了 0008。

因此,我希望能够检查数字是否为:

  1. 给定列中当前存在的范围。
  2. 如果有任何重复

我还想输出这些结果:

SKIPPED:
xxxx0007

DUPLICATES:
xxxx0008

我所能得到的最远的是用来awk获取我需要的列:

cat <file.txt> | awk '{ print $5 }'

这让我明白了这一点:

NAME
xxxx0001
xxxx0002
xxxx0003
xxxx0004
xxxx0005
xxxx0006
xxxx0008
xxxx0008

但我不知道从这里去哪里。

我是否需要遍历列表项并进行解析,以便仅获取数字,然后开始与下一行进行一些比较?

任何帮助将不胜感激谢谢!

4

1 回答 1

2

作为起点,请尝试以下方法:

awk '
NR>1 { gsub("[^0-9]", "", $5); count[$5]++ }
END {
    print "Skipped:"
    for (i=1; i<NR; i++)
        if (count[i] == 0) printf "xxxx%04d\n", i
    print "Duplicates:"
    for (i=1; i<NR; i++)
        if (count[i] > 1) printf "xxxx%04d\n", i
} ' file.txt

输出:

Skipped:
xxxx0007
Duplicates:
xxxx0008
  • 该条件NR>1用于跳过顶部标题行。
  • gsub("[^0-9]", "", $5)从 中删除非数字字符$5。结果,$5设置为从第 5 列中提取的数字。
  • 该数组count[]计算每个数字的出现次数。如果该值为0(或未定义),则表示该数字被跳过。如果该值大于1,则该数字是重复的。
  • 在处理完所有输入行后执行该END { ... }块,报告最终结果很有用。

但是,“跳过/重复”方法无法很好地检测到以下情况:

#       LOCATION        TIME REFERENCE      UNITS       NAME            COMMENTS
1       0:00.500        24000               Samples     xxxx0001
2       0:02.888        138652              Samples     xxxx0003
3       0:04.759        228446              Samples     xxxx0004
4       0:07.050        338446              Samples     xxxx0005
5       0:09.034        433672              Samples     xxxx0006
6       0:12.061        578958              Samples     xxxx0007
7       0:14.111        677333              Samples     xxxx0008
8       0:17.253        828181              Samples     xxxx0009

或者

#       LOCATION        TIME REFERENCE      UNITS       NAME            COMMENTS
1       0:00.500        24000               Samples     xxxx0001
2       0:02.888        138652              Samples     xxxx0003
3       0:04.759        228446              Samples     xxxx0002
4       0:07.050        338446              Samples     xxxx0004
5       0:09.034        433672              Samples     xxxx0005
6       0:12.061        578958              Samples     xxxx0006
7       0:14.111        677333              Samples     xxxx0007
8       0:17.253        828181              Samples     xxxx0008

最好在期望值和实际值之间进行逐行比较。那么怎么样:

awk '
NR>1 {
    gsub("[^0-9]", "", $5)
    if ($5 != NR-1) printf "Line: %d  Expected: xxxx%04d  Actual: xxxx%04d\n", NR, NR-1, $5
} ' file.txt

原始示例的输出:

Line: 8  Expected: xxxx0007  Actual: xxxx0008

[编辑]

根据包含更多额外标题行的修改后的输入文件,如何:

awk '
f {
    gsub("[^0-9]", "", $5)
    if ($5 != NR-skip) printf "Line: %d  Expected: xxxx%04d  Actual: xxxx%04d\n", NR, NR-skip, $5
}
/^#[[:blank:]]+LOCATION[[:blank:]]+TIME REFERENCE/ {
    skip = NR
    f = 1
}
' file.txt

输出:

Line: 19  Expected: xxxx0007  Actual: xxxx0008

上面的脚本会跳过这些行,直到# LOCATION TIME REFERENCE找到特定的模式。

  • 如果为真,则执行该f { ... }块。f所以块被跳过,直到f被设置为一个非零值。
  • /^# .../ { ... }如果输入行与模式匹配,则执行该块。如果找到,skip则设置为标题行数,并且 f(flag) 设置为 1,以便从下一次迭代开始执行上块。

希望这可以帮助。

于 2019-09-10T03:00:50.550 回答