使用Perl分割大型文本文件

3

我需要将一个1.8Tb的大型文本文件分为两个文件(我只需要文件的后半部分)。这个文件的记录分隔符是\n

我尝试过:

perl -ne 'print if $. >= $line_to_start_from' test.txt > result.txt 

我使用这个解决方案在一个大小为115Mb的小测试文件上可以完成工作,但需要22秒的时间。

对于一个1.8Tb的大文件使用这个解决方案将需要不合理长的时间,所以我的问题是是否有一种在Perl中分割巨大文件而不需要循环的方法?


3
使用tail呢? - choroba
4
你找到分割点的标准是什么?如果只是大致平均分割,就使用seek()函数定位到(-s $file) / 2位置,跳到下一个 \n(换行符)处,然后读取大块数据(例如1MB)并复制到输出。尽管如此,处理时间仍然会比较长。 - Dave Mitchell
1
有许多Unix工具旨在完成这种工作,但我想知道是否可以使用Perl完成此任务。因为该语言提供了更多的灵活性。 - Aditya
1
“split”通常是一个编译的C程序,旨在完成一项任务,并且可能使用低级(可能是特定于操作系统的)系统调用。涉及的层数比Perl脚本少得多。当然它会运行得更快。 - Shawn
2
“按行号计算” - 谢谢(是的,我注意到了,只是想确认一下)。那么,对于您的115 Mb文件,它运行多长时间:perl -we'$f=shift//die"need filename\n"; open $fh, $f; while (<$fh>) { last if $.>=10 }; print <$fh>' text.txt > result.txt(将“10”替换为所需的行号)?引用的22秒太长了。在一个123Mb的文件中,该单行程序花费了1.15秒,并提取200万行后的所有内容(大约一半 - 输出为62Mb)。这是在一个7-8年的老笔记本电脑上使用perl 5.16完成的。 - zdim
显示剩余8条评论
1个回答

4

默认情况下,Perl 逐行读取文件输入。如果您的文件包含大量相对较短的行(我假设是这种情况),那么与像 split 这样从文件一次性读取更大块内容的实用程序相比,Perl 将慢得多。

为了进行测试,我创建了一个非常短的行的 ~200MB 文件:

$ perl -e 'print "123\n" for( 1 .. 50_000_000 );' >file_to_split

split 可以比较合理地处理它:

$ time split --lines=25000000 file_to_split half

real    0m1.266s
user    0m0.314s
sys     0m0.213s

天真的perl方法速度慢得多:

$ time perl -ne 'print if $. > 25_000_000' file_to_split >second_half

real    0m10.474s
user    0m10.257s
sys     0m0.222s

但你可以使用$/特殊变量,让Perl一次读取多行。例如每次读取16 KB的数据:

my $CHUNK_SIZE = 16 * 1024;
my $SPLIT_AT_LINE = 25_000_000;

{
    local $/ = \$CHUNK_SIZE;
    my $lineNumber = 0;
    while ( <> ) {
        if ( $lineNumber > $SPLIT_AT_LINE ) {
            # everything from here on is in the second half
            print $_;
        }
        else {
            my $count = $_ =~ tr/\n/\n/;
            $lineNumber += $count;
            if ( $lineNumber > $SPLIT_AT_LINE ) {
                # we went past the split, get some of the lines from this buffer
                my $extra = $lineNumber - $SPLIT_AT_LINE;
                my @lines = split m/\n/, $_, $count - $extra + 1;
                print $lines[ -1 ];
            }
        }
    }
}

如果你不在意超出拆分几行,你可以使这段代码更简单。这使得perl在合理的时间内执行相同的操作:

$ time perl test.pl file_to_split >second_half

real    0m0.678s
user    0m0.095s
sys     0m0.297s

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接