重新格式化表格

重新格式化表格

我有一些表 ( table.txt) 被错误地构建并在结果中出现冗余,如下所示:

YEAR MONTH DAY RES
1971 1     1   245
1971 1     2   587
...
1971 12    31  685
1971 1     1   245
1971 1     2   587
...
1971 12    31  685
1972 1     1   549
1972 1     2   746
...

相反,我想要:

YEAR MONTH DAY RES
1971 1     1   245
1971 1     2   587
...
1971 12    31  685
1972 1     1   549
1972 1     2   746
...

所以问题是结果在表中出现了两次。这意味着(使用提供的示例)在“1971”之后,我应该预期年份“1972”,而不是再次“1971”。有没有办法使用 sh/bash 删除多余的结果?

我必须注意到,我的数据从 1971 年一直持续到 2099 年,即使在 2000 年之后,它们的格式也完全相同,如下所示:

YEAR MONTH DAY RES
1971 1     1   245
1971 1     2   587
...
2000 1     1   875
2000 1     2   456
...
2099 12    31  321

答案1

这是两个互斥的sed循环:

sed -ne'p;/ 12 * 31 /!d;:n' -e'n;//!bn' <<""
YEAR MONTH DAY RES
1971 1     1   245
1971 1     2   587
...
1971 12    31  685
1971 1     1   245
1971 1     2   587
...
1971 12    31  685
1972 1     1   549
1972 1     2   746
...
1972 12    31  999
1972 1     1   933
1972 1     2   837
...
1972 12    31  343

YEAR MONTH DAY RES
1971 1     1   245
1971 1     2   587
...
1971 12    31  685
1972 1     1   549
1972 1     2   746
...
1972 12    31  999

基本上sed有两种状态 - print 和。在第一个状态 - print 状态 -sed自动print 每个输入行,然后根据模式检查它/ 12 * 31 /。如果当前模式空间不!匹配,它将被d删除并sed拉入下一个输入行,并在 rint 命令处从顶部再次启动脚本,p而根本不尝试运行delete 命令后面的任何内容。

当输入线/ 12 * 31 /然而,matchsed进入了脚本的后半部分 -环形。首先它定义了一个:名为 的分支标签n;然后它用 ext 输入行覆盖当前模式空间n,然后将当前模式空间与//最后一个匹配的模式进行比较。因为之前匹配的行刚刚被 ext 覆盖n,所以这个的第一次迭代循环不匹配,并且每次它不!回溯sed b:n标签以获取next 输入行并再次将其与//最后一个匹配的模式进行比较。

当最终进行另一个匹配时(大约 365 个next 行之后),当它完成其脚本时,sed不会自动打印它,拉入下一个输入行,并在第一个状态下的 rint 命令处-n从顶部重新开始。p因此,每个循环状态都会在同一键上跳转到下一个状态,并同时尽可能少地查找下一个键。

请注意,整个脚本无需调用单个编辑例程即可完成,并且只需要编译单个正则表达式。生成的自动机非常简单 - 它只理解[123 ][^123 ]。更重要的是,至少一半的比较很可能是在没有任何编译的情况下进行的,因为在循环根本就是//空循环。因此,可以通过每个输入行sed一次调用来完全完成该循环。regexec()sed 可能p对rint 循环也做类似的事情。


定时的


我很好奇这里的各种答案可能会如何表现,所以我想出了自己的表格:

dash <<""
    d=0 D=31 IFS=: set 1970 1
    while   case  "$*:${d#$D}" in (*[!:]) ;;
            ($(($1^($1%4)|(d=0))):1:)
                     D=29 set $1 2;;
            (*:1:)   D=28 set $1 2;;
            (*[3580]:)
                     D=30 set $1 $(($2+1));;
            (*:)     D=31 set $(($1+!(t<730||(t=0)))) $(($2%12+1))
            esac
    do      printf  '%-6d%-4d%-4d%d\n' "$@" $((d+=1)) $((t+=1))
    done|   head    -n1000054 >/tmp/dates

dash <<<''  6.62s user 6.95s system 166% cpu 8.156 total

这将输入 100 万多行,/tmp/dates并将 1970 - 3338 年每年的输出翻倍。该文件如下所示:

tail -n1465 </tmp/dates | head; echo; tail </tmp/dates

3336  12  27  728
3336  12  28  729
3336  12  29  730
3336  12  30  731
3336  12  31  732
3337  1   1   1
3337  1   2   2
3337  1   3   3
3337  1   4   4
3337  1   5   5

3338  12  22  721
3338  12  23  722
3338  12  24  723
3338  12  25  724
3338  12  26  725
3338  12  27  726
3338  12  28  727
3338  12  29  728
3338  12  30  729
3338  12  31  730

……无论如何,有一些。

然后我尝试了不同的命令:

for  cmd in "sort -uVk1,3" \
            "sed -ne'p;/ 12 * 31 /!d;:n' -e'n;//!bn'" \
            "awk '"'{u=$1 $2 $3 $4;if (!a[u]++) print;}'\'
do   eval   "time ($cmd|wc -l)" </tmp/dates
done

500027
( sort -uVk1,3 | wc -l; ) \
1.85s user 0.11s system 280% cpu 0.698 total

500027
( sed -ne'p;/ 12 * 31 /!d;:n' -e'n;//!bn' | wc -l; ) \
0.64s user 0.09s system 110% cpu 0.659 total

500027
( awk '{u=$1 $2 $3 $4;if (!a[u]++) print;}' | wc -l; ) \
1.46s user 0.15s system 104% cpu 1.536 total

sort和命令sed都在不到一半的时间内完成awk- 这些结果是典型的。我确实运行了它们好几次。看起来所有命令都写出了正确的行数 - 所以它们可能都有效。

sort每次运行的完成时间都相当不错(通常领先一点),但比sed其他两个命令中的任何一个都需要做更多的实际工作来实现其结果。它正在运行并行作业来完成其任务,并从我的多核 CPU 中受益匪浅。并且在它们处理的整个时间里都与分配给它们的单核挂钩。sedsortawksed

这里的结果来自标准的、最新的 GNU sed,但我确实尝试了另一个。事实上,我用其他二进制文件尝试了所有三个命令,但只有该sed命令真正适用于我的传家宝工具。我猜想,由于非标准语法,其他人只是在开始工作之前就因错误而退出。

尽可能使用标准语法是件好事——在许多情况下,您可以自由地使用更简单、更完善、更高效的实现:

PATH=/usr/heirloom/bin/posix2001:$PATH; time ...

500027
( sed -ne'p;/ 12 * 31 /!d;:n' -e'n;//!bn' | wc -l; ) \
0.31s user 0.12s system 136% cpu 0.318 total

答案2

尝试通过管道将其传输到 awk

awk '!a[$0]++' files.txt > new_files.txt
mv new_files.txt files.txt

这只会输出一次行。

编辑:(不确定连接 var 是否可以解决问题)

awk '{u=$1 $2 $3 $4 ; if ( !a[u]++ ) print ; } ' ...

答案3

$ (head -1 table.txt ; tail -n +2 table.txt | sort -u -V -k1,3)
YEAR MONTH DAY RES
1971 1     1   245
1971 1     2   587
1971 2     1   587
1971 12    31  685
1972 1     1   549
1972 1     2   746
2000 1     1   875
2000 1     2   456
2099 12    31  321

相关内容