使用 Perl 拆分大文本文件
Splitting large text files with Perl
我必须将一个 1.8Tb 的大文本文件一分为二(我只需要文件的后半部分)。该文件将 \n
作为记录分隔符。
我试过了
perl -ne 'print if $. >= $line_to_start_from' test.txt > result.txt
在一个小得多的 115Mb 测试文件上,它完成了工作,但花了 22 秒。
对 1.8Tb 文件使用此解决方案将花费不合理的长时间,所以我的问题是 Perl 中是否有一种方法可以拆分大文件而不循环处理它们?
默认情况下,perl 一次读取一行文件输入。如果你的文件包含很多相对较短的行(我假设它确实如此),perl 将比 split
这样的实用程序慢很多,后者一次从文件中读取更大的块。
为了测试,我创建了一个 ~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
我必须将一个 1.8Tb 的大文本文件一分为二(我只需要文件的后半部分)。该文件将 \n
作为记录分隔符。
我试过了
perl -ne 'print if $. >= $line_to_start_from' test.txt > result.txt
在一个小得多的 115Mb 测试文件上,它完成了工作,但花了 22 秒。
对 1.8Tb 文件使用此解决方案将花费不合理的长时间,所以我的问题是 Perl 中是否有一种方法可以拆分大文件而不循环处理它们?
默认情况下,perl 一次读取一行文件输入。如果你的文件包含很多相对较短的行(我假设它确实如此),perl 将比 split
这样的实用程序慢很多,后者一次从文件中读取更大的块。
为了测试,我创建了一个 ~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