在Perl或Python中按列值将100亿行文件拆分为5,000个文件

时间:2016-04-14 18:06:22

标签: python perl file-processing bigdata

我有一个100亿行制表符分隔文件,我希望根据列(第一列)拆分为5,000个子文件。如何在Perl或Python中有效地完成这项工作?

之前已经在这里询问过,但所有方法都会为每行读取打开一个文件,或者将所有数据都放在内存中。

3 个答案:

答案 0 :(得分:2)

awk救援!

awk 'f!=$1{close(f)} {f=$1; print >> f}' file

它将逐行处理,一次打开一个文件。

如果将原始文件拆分为块,可以更有效地并行完成,并合并生成的文件(如果需要保留订单则需要标记它们)

答案 1 :(得分:2)

您可以保持哈希(关联数组)映射列值以打开输出文件句柄,并且只有在尚未为该列值打开时才打开输出文件。

除非您达到最大打开文件数限制,否则这样就足够了。 (使用ulimit -Hnbash中查看。)如果这样做,您需要关闭文件句柄(例如随机文件句柄,或者使用时间最长的文件句柄,这很容易跟踪另一个哈希),或者你需要在输入中进行多次传递,只处理尽可能多的列值,因为你可以在一次传递中打开输出文件并在将来的传递中跳过它们。

答案 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;