[Perl]按行倒序输出大体积文本文件(假设文件10G以上)

There's more than one way to do it!
https://metacpan.org http://perlmonks.org
回复
头像
paktc
出类拔萃
出类拔萃
帖子: 65
注册时间: 2016年07月21日 20:34
联系:

[Perl]按行倒序输出大体积文本文件(假设文件10G以上)

帖子 paktc »

假设文件大于10G,内存1G

方案:倒序,线性读取
实践总结:初始方案是从文件末尾开始,反方向逐个字节读取存入$buff变量,遇到换行符时将$buff写出并清空。
但是这种逐字节读取效率极低。改进方案是设置一定的缓存大小(例如2^16 => 65536),逐段地读入,判断是否有换行符,有则切割 -> reverse 并输出。
=info
文本按行倒序输出
Paktc/Vicyang
2019-01
=cut

use strict;
use Fcntl qw(:seek);
STDOUT->autoflush(1);
my $src = "F:/A_Parts.txt";
my $dst = $src;
$dst =~s/(\.\w+)$/_REV$1/;

reverse_write( $src, $dst );

sub reverse_write
{
my ($srcfile, $dstfile) = @_;
open my $SRC, "<:raw", $srcfile or die "$!\n";
open my $DST, ">:raw", $dstfile or die "$!\n";

# 缓冲区大小
my $buffsize = 2**16;
my $offset = -s $SRC;
my $buff;
my @lines;
my $left = "";
while ( $offset >= $buffsize )
{
$offset -= $buffsize;
seek $SRC, $offset, SEEK_SET;
read $SRC, $buff, $buffsize;
# 拼接,考虑单行文本小于 $buffsize 的情况
$buff = $buff . $left;
if ( $buff =~/\r?\n/ ) {
@lines = reverse( split /\r?\n/, $buff, -1 );
$left = pop @lines;
printf $DST "%s\r\n", join("\r\n", @lines);
} else {
$left = $buff;
#printf "%s\n", $left;
}
}

# 如果 offset 未归零,读取剩下(源文件的开头)部分
return if ($offset <= 0);
seek $SRC, 0, SEEK_SET;
read $SRC, $buff, $offset;
@lines = reverse(split /\r?\n/, $buff .$left );
print $DST join("\r\n", @lines);
close $SRC;
close $DST;
}
回复

在线用户

正浏览此版面之用户: 没有注册用户 和 5 访客