删除重复的行(包括重复的行)

时间:2013-04-30 17:04:36

标签: python perl unix

我需要删除文件中多次出现的所有行。

示例:

Line1
Line2
Line3
Line2

结果:

Line1
Line3

Python,Perl或unix-util并不重要。谢谢。

6 个答案:

答案 0 :(得分:4)

保留顺序,但在内存中保留两份文件:

my @lines;
my %seen;
while (<>) {
   push @lines, $_;
   ++$seen{$_};
}

for (@lines) {
   print if $seen{$_} == 1;
}

作为一个单行:

perl -ne'push @l, $_; ++$s{$_}; }{ for (@l) { print if $s{$_} == 1; }'

不保留顺序,但只在内存中保留文件的一个副本:

my %seen;
++$seen{$_} while <>;

while (my ($k, $v) = each(%seen)) {
   print $k if $v == 1;
}

作为一个单行:

perl -ne'++$s{$_}; }{ while (my ($k, $v) = each(%s)) { print $k if $v == 1; }'

答案 1 :(得分:2)

这是一个Python实现。

如果您需要保留行的初始顺序:

import collections
import fileinput

lines = list(fileinput.input())
counts = collections.Counter(lines)
print(''.join(line for line in lines if counts[line] == 1))

如果没有,它会更简单,更快一点):

import collections
import fileinput

counts = collections.Counter(fileinput.input())
print(''.join(line for line, count in counts.iteritems() if count==1))

对于每一行,您需要查看它是否有任何重复。如果您不想以二次方式执行此操作(执行一次传递,然后对每一行执行第二次传递),则需要使用允许您在两次线性传递中执行此操作的中间数据结构。

因此,您在列表中进行传递以构建哈希表(collections.Counter是一个专门的dict,它只是将每个键映射到它出现的次数)。然后,您可以在列表中进行第二次传递,在哈希表(第一个版本)中查找每个传递,或者只是迭代哈希表(第二个)。


据我所知,没有办法与命令行工具相提并论;你将至少必须sort输入(即O(N log N),而不是O(N)),或使用隐式执行等效的工具。

但对于许多用例来说,这不是什么大问题。对于具有1M行的80MB文件,N log N仅比N慢一个数量级,并且完全可以想象两个工具之间的常数乘数差异将在相同的顺序上。


快速计时测试验证,在1M线的范围内,sort | uniq -u版本的速度只有6倍以上,但仍然足够快,您可能不会关心(10秒以下,这是更多的时间)比复制和粘贴Python代码要好,对吧?)除非你必须反复这样做。

从进一步的测试中,在128K行,Python版本只快4倍; 64M线,速度快28倍;在5G线路上......这两个版本都使得系统陷入交换,严重到我杀死了测试。 (用Counter键值数据库替换dbm解决了这个问题,但是对于较小的比例而言代价很高。)

答案 2 :(得分:1)

* nix命令 uniq 可以执行此操作。

sort file.name | uniq -u

答案 3 :(得分:1)

这是perl中的一个例子:

my %line_hash;
open my $fh, "<", "testfile";
while(my $line = <$fh>) {
   $line_hash{$line}++; 
}
close $fh;

open my $out_fh, ">>", "outfile";
for my $key ( sort keys %line_hash ){
    print $out_fh $key if $line_hash{$key} == 1;
}
close $out_fh;

testfile的:

$ cat testfile
Line1
Line2
Line3
Line2

OUTFILE:

$ cat outfile
Line1
Line3

答案 4 :(得分:0)

sort inputfile | uniq -u

(假设gnu coreutils uniq)

虽然SUSv4说:

  

-u      禁止写入在输入中重复的行。

从评论到一些答案听起来并非所有uniq都以同样的方式解释。

答案 5 :(得分:-1)

读取每一行,grep同一文件中的行以查找计数,只打印计数为1的那些:

#!/bin/bash
while read line
do
  if [ `grep -c ${line} sample.txt` -eq 1 ] ; then echo ${line} ; fi
done < sample.txt