我有一个100亿行制表符分隔文件,我希望根据列(第一列)拆分为5,000个子文件。如何在Perl或Python中有效地完成这项工作?
之前已经在这里询问过,但所有方法都会为每行读取打开一个文件,或者将所有数据都放在内存中。
答案 0 :(得分:2)
awk
救援!
awk 'f!=$1{close(f)} {f=$1; print >> f}' file
它将逐行处理,一次打开一个文件。
如果将原始文件拆分为块,可以更有效地并行完成,并合并生成的文件(如果需要保留订单则需要标记它们)
答案 1 :(得分:2)
您可以保持哈希(关联数组)映射列值以打开输出文件句柄,并且只有在尚未为该列值打开时才打开输出文件。
除非您达到最大打开文件数限制,否则这样就足够了。 (使用ulimit -Hn
在bash
中查看。)如果这样做,您需要关闭文件句柄(例如随机文件句柄,或者使用时间最长的文件句柄,这很容易跟踪另一个哈希),或者你需要在输入中进行多次传递,只处理尽可能多的列值,因为你可以在一次传递中打开输出文件并在将来的传递中跳过它们。
答案 2 :(得分:1)
这个程序可以按照你的要求进行。它期望输入文件作为命令行上的参数,并写入输出文件,其名称取自输入文件记录的第一列
它保留文件句柄的哈希%fh
和标志的并行哈希%opened
,以指示之前是否曾打开过给定文件。如果文件出现在%opened
哈希中,则打开文件以进行追加,如果之前从未打开过,则打开文件进行写入。如果打开了打开文件的限制,则关闭(随机)选择1,000个文件句柄。跟踪上次使用每个句柄的时间并关闭最过时的句柄是没有意义的:如果输入文件中的数据是随机排序的,那么散列中的每个句柄都有相同的机会成为下一个使用的句柄或者,如果数据已经排序,那么将不再使用任何文件句柄
use strict;
use warnings 'all';
my %fh;
my %opened;
while ( <> ) {
my ($tag) = split;
if ( not exists $fh{$tag} ) {
my $mode = $opened{$tag} ? '>>' : '>';
while () {
eval {
open $fh{$tag}, $mode, $tag or die qq{Unable to open "$tag" for output: $!};
};
if ( not $@ ) {
$opened{$tag} = 1;
last;
}
die $@ unless $@ =~ /Too many open files/;
my $n;
for my $tag ( keys %fh ) {
my $fh = delete $fh{$tag};
close $fh or die $!;
last if ++$n >= 1_000 or keys %fh == 0;
}
}
}
print { $fh{$tag} } $_;
}
close $_ or die $! for values %fh;