我需要删除文件中多次出现的所有行。
示例:
Line1
Line2
Line3
Line2
结果:
Line1
Line3
Python,Perl或unix-util并不重要。谢谢。
答案 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