我有一个文件,其中每行都包含模式,其中一些正在重复。我只想要那些不重复的模式。所以,我想删除所有重复数据以及原始模式。我不能使用排序,因为我想模式是在相同的顺序。删除重复的行以及原始文件
文件:
foo1
foo2
foo3
foo2
foo4
foo1
foo1
foo5
所需的输出:
foo3
foo4
foo5
因为它是一个大的文件(大约1GB),我喜欢的东西非常快。由于事先
我有一个文件,其中每行都包含模式,其中一些正在重复。我只想要那些不重复的模式。所以,我想删除所有重复数据以及原始模式。我不能使用排序,因为我想模式是在相同的顺序。删除重复的行以及原始文件
文件:
foo1
foo2
foo3
foo2
foo4
foo1
foo1
foo5
所需的输出:
foo3
foo4
foo5
因为它是一个大的文件(大约1GB),我喜欢的东西非常快。由于事先
执行此操作的最简单方法是遍历文件两次,计算第一次出现线条的频率并在第二次出现时打印出唯一的线条。
如果你有足够的RAM(这将花费相当多的),你可以使用
awk 'NR == FNR { seen[$0]++; next } seen[$0] == 1' file file
多少内存,这需要依赖于文件中的行的平均长度。如果行很短,哈希映射的开销会使内存使用量远远超过纯输入数据所需的1GB。我最近有一个类似的使用案例,其中awk最终使用了超过8GB的RAM,用于〜300MB的输入数据,其中行的平均长度为8个字符。用C++重写代码使得问题不那么严重,但仍然不切实际。
我们最终解决了sqlite的交易速度问题。对于您的使用情况,这可能最终成为
rm lcount.db
awk -v q=\' '
NR == 1 {
print "CREATE TABLE lines (line text PRIMARY KEY, counter INTEGER, nr INTEGER);"
}
{
sub(q, q q); # hacky way to sanitize lines with quotes in them
print "INSERT OR IGNORE INTO lines VALUES (" q $0 q ", 0, " NR ");";
print "UPDATE lines SET counter = counter + 1 WHERE line = " q $0 q ";"
}
END {
print "SELECT line FROM lines WHERE counter = 1 ORDER BY nr;"
}' file | sqlite3 lcount.db
令人惊讶的是,这仍然相当快。它的速度取决于你的可用内存--sqlite进程只用几兆字节,但速度很大程度上取决于文件系统缓存数据库文件的可用空间。
我对SQL的卫生设备并不满意,请介意一下;我不相信如果输入数据来自不可靠的来源,这是完全安全的。倘若这是一种担心,你可以使用以下命令:
perl -MDBI -e'
my $dbh = DBI->connect("dbi:SQLite:dbname=lcount.db", "", "", { PrintError=>0, RaiseError=>1 });
$dbh->do("CREATE TABLE lines (line TEXT PRIMARY KEY, counter INTEGER, nr INTEGER)");
my $ins_sth = $dbh->prepare("INSERT OR IGNORE INTO lines VALUES (?, 0, ?)");
my $upd_sth = $dbh->prepare("UPDATE lines SET counter = counter + 1 WHERE line = ?");
while (<>) {
$ins_sth->execute($_, $.);
$upd_sth->execute($_);
}
my $sth = $dbh->prepare("SELECT line FROM lines WHERE counter = 1 ORDER BY nr");
print while ($_) = $sth->fetchrow_array;
' file
一个很好的创造性的解决方案! –
一种可能的解决办法是这样的:
$ awk 'NR==FNR{++seen[$0];next}seen[$0]==1' file file
foo3
foo4
foo5
它读取文件两次,共每一行的出现次数的保持轮第一时间和打印独特线的第二时间。
另一种选择,它使用更多的存储器,但只读取该文件一次:
$ awk '{++seen[$0];a[NR]=$0}END{for(i=1;i<=NR;++i)if(seen[a[i]]==1)print a[i]}' file
foo3
foo4
foo5
这还存储阵列a
中的每一行,所以不是重新读取该文件,环路可以用来打印独特的线条。
我不知道如何做到这一点的油烟机(我想这对内存的要求可能是相似的)下工作,但你也可以使用一些标准的工具:
$ sort file | uniq -u | grep -Fxf - file
foo3
foo4
foo5
sort file | uniq -u
获得独特的线条和将它们传递给grep,作为匹配的模式列表。 -F
开关匹配固定字符串,而-x
表示仅打印与整个图案匹配的行。
我无法排序。我需要按顺序排列模式。 –
@valuable_asset对'grep'的输入进行排序,但输出不是,所以这不是问题。 –
如果有很多重复的线的这可能表现良好,
perl -ne'
$h{$_}++ or push @r,$_;
END {
$h{$_} <2 and print for @r
}
' file
它通过循环文件并存储在%h
散列算的相同的行,而填充@r
阵列具有独特的线。在文件处理结束时,它会循环低谷@r
并仅打印出现少于两次的行。
我对perl有点新鲜。你能解释一下代码吗? –
@valuable_asset检查更新 –
你的问题的核心是这样的 - 因为你需要删除原来的,直到你知道这是一个傻瓜,你必须保持它在内存中,直到整个文件被解析。
有两种方法可以从根本上做到这一点 - 将所有内容存储在内存中或从磁盘读取文件两次。
因此,在perl中 - 读入内存(将使用多个的原始文件大小,由于管理费用)。
#!/usr/bin/perl
use strict;
use warnings;
open (my $input_fh, "<", "data_file_name") or die $!;
my @data = <$input_fh>;
close ($input_fh):
my %count_of;
$count_of{$_}++ for @data;
foreach my $line (@data) {
print $line if $count_of{$line} <= 1;
}
读文件两次 - 将需要更长的时间,因为磁盘IO,但较低的内存使用情况(取决于有多少副本有一个位)。
#!/usr/bin/perl
use strict;
use warnings;
open(my $input_fh, "<", "data_file_name") or die $!;
my %count_of;
$count_of{$_}++ for <$input_fh>;
seek($input_fh, 0, 0); #rewind - could close/reopen instead.
foreach my $line (<$input_fh>) {
print $line if $count_of{$line} <= 1;
}
close($input_fh);
注意 - 在上述两种情况中,我们都是直接使用该行 - 包括空格和换行符。所以:"foo "
和"foo"
将被认为是不同的。您可以使用“sed like”搜索轻松处理并替换,例如s/\s+//g
删除空格。
Perl解决方案。该程序需要输入文件的路径作为命令行上的参数
您问题中的数据具有可变数量的尾随空格。我假定你不需要对它们进行比较
1GB不是一个文件,并处理它是将其读入内存的最快方法这么大之前修剪这些。该解决方案保持哈希建立的独特性和阵列,以维持秩序
use strict;
use warnings;
my (%count, @lines);
$count{$_}++ or push @lines, $_ while <>;
print grep $count{$_} == 1, @lines;
输出
foo3
foo4
foo5
Tcl中解决这个最简单的方法是使用字典,因为他们保持秩序插入钥匙。特别是dict incr
和dict for
是非常有用的。作为标准输入→标准输出滤波器...
set seen {}
while {[gets stdin line] >= 0} {
dict incr seen $line
}
dict for {line count} $seen {
if {$count == 1} {
puts $line
}
}
这将使用存储器成正比不同线的数量,并且将读出的输入恰好一次;要尽量少地满足问题要求是非常困难的,因为在找到一条线的重复之前可能有任意数量的行要读取。
RAM是一个问题吗? – Wintermute
你不喜欢为这项任务获得特别快的速度。你最好有很多的RAM。 –
您可以将所有行加载到散列中,其中键是行content.and值是出现次数。然后提取值等于1的所有键,唯一的键。 –