成对删除重复行?

成对删除重复行?

我今天遇到了这个用例。乍一看似乎很简单,但是摆弄sortuniqsed就会awk发现它并不简单。

怎么才能全部删除重复行?换句话说,如果给定行有偶数个重复项,则将其全部删除;如果有奇数个重复行,则删除除一行之外的所有行。 (可以假设已排序的输入。)

干净优雅的解决方案是更好的选择。

输入示例:

a
a
a
b
b
c
c
c
c
d
d
d
d
d
e

输出示例:

a
d
e

答案1

sed我在发布这个问题后不久就找到了答案;sed到目前为止还没有其他人使用过,所以这里是:

sed '$!N;/^\(.*\)\n\1$/d;P;D'

稍微解决一下更普遍的问题(删除三行、四行或五行怎么样?)提供了以下可扩展的解决方案:

sed -e ':top' -e '$!{/\n/!{N;b top' -e '};};/^\(.*\)\n\1$/d;P;D' temp

扩展以删除三重行:

sed -e ':top' -e '$!{/\n.*\n/!{N;b top' -e '};};/^\(.*\)\n\1\n\1$/d;P;D' temp

或者删除四边形线:

sed -e ':top' -e '$!{/\n.*\n.*\n/!{N;b top' -e '};};/^\(.*\)\n\1\n\1\n\1$/d;P;D' temp

sed与大多数其他选项相比,它还有一个额外的优势,那就是它能够真正在流中操作,不需要比要检查重复的实际行数更多的内存存储。


作为cuonglm在评论中指出,将区域设置设置为 C 是必要的,以避免无法正确删除包含多字节字符的行。所以上面的命令就变成了:

LC_ALL=C sed '$!N;/^\(.*\)\n\1$/d;P;D' temp
LC_ALL=C sed -e ':top' -e '$!{/\n/!{N;b top' -e '};};/^\(.*\)\n\1$/d;P;D' temp
LC_ALL=C sed -e ':top' -e '$!{/\n.*\n/!{N;b top' -e '};};/^\(.*\)\n\1\n\1$/d;P;D' temp
# Etc.

答案2

它不是很优雅,但它是我能想到的最简单的:

uniq -c input | awk '{if ($1 % 2 == 1) { print substr($0, 9) }}'

substr() 只是修剪输出uniq。这将一直有效,直到一行的重复次数超过 9,999,999 个(在这种情况下,uniq 的输出可能会溢出 9 个字符)。

答案3

尝试一下awk下面的脚本:

#!/usr/bin/awk -f
{
  if ((NR!=1) && (previous!=$0) && (count%2==1)) {
    print previous;
    count=0;
  }
  previous=$0;
  count++;
}
END {
  if (count%2==1) {
    print previous;
  }
}

假设lines.txt文件已排序。

考试:

$ chmod +x script.awk
$ ./script.awk lines.txt
a
d
e

答案4

如果输入已排序:

perl -0pe  'while(s/^(.*)\n\1\n//m){}'

相关内容