0

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

例子:

Line1
Line2
Line3
Line2

结果:

Line1
Line3

Python、Perl 或 unix-util 无关紧要。谢谢你。

4

6 回答 6

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; }'
于 2013-04-30T17:59:13.647 回答
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 线路上……这两个版本都将系统驱动到交换系统的严重程度,以至于我终止了测试。(Counterdbm键值数据库替换 可以解决这个问题,但对于较小的规模来说成本很高。)

于 2013-04-30T17:08:06.123 回答
1

*nix 命令uniq可以做到这一点。

sort file.name | uniq -u
于 2013-04-30T17:13:32.970 回答
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;

测试文件:

$ cat testfile
Line1
Line2
Line3
Line2

输出文件:

$ cat outfile
Line1
Line3
于 2013-04-30T17:23:39.460 回答
0
sort inputfile | uniq -u

(假设 gnu coreutils uniq)

虽然SUSv4说:

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

从评论到某些答案,并非所有 uniq 都以相同的方式解释。

于 2013-04-30T17:18:57.943 回答
-1

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

#!/bin/bash
while read line
do
  if [ `grep -c ${line} sample.txt` -eq 1 ] ; then echo ${line} ; fi
done < sample.txt
于 2013-04-30T17:17:54.820 回答