在 Perl 或 Python 中按列值将 100 亿行文件拆分为 5,000 个文件
split 10 billion line file into 5,000 files by column value in Perl or Python
我有一个 100 亿行制表符分隔的文件,我想根据一列(第一列)拆分为 5,000 个子文件。我怎样才能在 Perl 或 Python 中有效地做到这一点?
之前已经在这里问过这个问题,但是所有的方法都会为读取的每一行打开一个文件,或者它们将所有数据都放在内存中。
awk
救援!
awk 'f!={close(f)} {f=; print >> f}' file
它将逐行处理,一次打开一个文件。
如果将原始文件拆分成块,这可以更有效地并行完成并合并生成的文件(如果需要保留顺序,则需要标记它们)
您可以保留哈希(关联数组)映射列值以打开输出文件句柄,并且仅当 none 已为该列值打开时才打开输出文件。
除非您达到打开文件的最大数量限制,否则这已经足够了。 (使用 ulimit -Hn
在 bash
中看到它。)如果你这样做,要么你需要关闭文件句柄(例如,一个随机的,或者最长时间没有被使用的那个,这很容易以在另一个哈希中跟踪),或者您需要对输入进行多次传递,只处理一次传递中可以打开输出文件的尽可能多的列值,并在以后的传递中跳过它们。
这个程序会按照你的要求去做。它期望输入文件作为命令行上的参数,并写入名称取自输入文件记录第一列的输出文件
它保留文件句柄的散列 %fh
和标志的并行散列 %opened
,这些标志指示给定文件之前是否曾被打开过。如果文件出现在 %opened
散列中,则打开该文件进行追加,如果以前从未打开过,则打开该文件进行写入。如果达到打开文件的限制,则关闭(随机)选择的 1,000 个文件句柄。跟踪每个句柄上次使用的时间并关闭最过时的句柄是没有意义的:如果输入文件中的数据是随机排序的,那么散列中的每个句柄都有相同的机会成为下一个被使用的对象,或者,如果数据已经排序,则 none 的文件句柄将再次使用
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;
我有一个 100 亿行制表符分隔的文件,我想根据一列(第一列)拆分为 5,000 个子文件。我怎样才能在 Perl 或 Python 中有效地做到这一点?
之前已经在这里问过这个问题,但是所有的方法都会为读取的每一行打开一个文件,或者它们将所有数据都放在内存中。
awk
救援!
awk 'f!={close(f)} {f=; print >> f}' file
它将逐行处理,一次打开一个文件。
如果将原始文件拆分成块,这可以更有效地并行完成并合并生成的文件(如果需要保留顺序,则需要标记它们)
您可以保留哈希(关联数组)映射列值以打开输出文件句柄,并且仅当 none 已为该列值打开时才打开输出文件。
除非您达到打开文件的最大数量限制,否则这已经足够了。 (使用 ulimit -Hn
在 bash
中看到它。)如果你这样做,要么你需要关闭文件句柄(例如,一个随机的,或者最长时间没有被使用的那个,这很容易以在另一个哈希中跟踪),或者您需要对输入进行多次传递,只处理一次传递中可以打开输出文件的尽可能多的列值,并在以后的传递中跳过它们。
这个程序会按照你的要求去做。它期望输入文件作为命令行上的参数,并写入名称取自输入文件记录第一列的输出文件
它保留文件句柄的散列 %fh
和标志的并行散列 %opened
,这些标志指示给定文件之前是否曾被打开过。如果文件出现在 %opened
散列中,则打开该文件进行追加,如果以前从未打开过,则打开该文件进行写入。如果达到打开文件的限制,则关闭(随机)选择的 1,000 个文件句柄。跟踪每个句柄上次使用的时间并关闭最过时的句柄是没有意义的:如果输入文件中的数据是随机排序的,那么散列中的每个句柄都有相同的机会成为下一个被使用的对象,或者,如果数据已经排序,则 none 的文件句柄将再次使用
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;