在Perl中从大文件中读取特定行

8

有没有一种快速、内存高效的方法可以读取大文件的特定行,而不需要将其加载到内存中?

我写了一个perl脚本,它运行了许多forks,我希望它们从文件中读取特定的行。

目前,我正在使用外部命令:

sub getFileLine {
    my ( $filePath, $lineWanted ) = @_;
    $SIG{PIPE} = '_IGNORE_';
    open( my $fh, '-|:utf8', "tail -q -n +$lineWanted \"$filePath\" | head -n 1" );
    my $line = <$fh>;
    close $fh;
    chomp( $line );
    return $line;
}

它很快也很有效 - 但或许有一种更“Perl风格”的方式,既像这种方法一样快速又能更高效利用内存?

正如您所知,在Perl中创建一个fork进程会复制主进程的内存 - 因此,如果主进程正在使用10MB,fork至少也会使用这么多。

我的目标是尽可能地减少fork进程(包括运行forks之前的主进程)的内存使用。这就是为什么我不想将整个文件加载到内存中的原因。


2
顺便说一下,它是IGNORE,而不是_IGNORE_ - ikegami
3个回答

19

在进一步之前,了解 fork 的工作原理非常重要。当你 fork 一个进程时,操作系统使用写时复制语义来共享父进程和子进程的大部分内存;只需要单独分配父进程和子进程之间差异的内存。

如果要在Perl中读取文件的单个行,这里有一个简单的方法:

open my $fh, '<', $filePath or die "$filePath: $!";
my $line;
while( <$fh> ) {
    if( $. == $lineWanted ) { 
        $line = $_;
        last;
    }
}

这里使用了特殊的$.变量,它保存了当前文件句柄的行号。


4

我认为Tie::File在内存使用上效率不高。难道OP没有要求低内存使用吗? - Zaid
@Zaid,实际上它的内存效率相当高;它不会将整个文件内容存储在内存中,只会存储每行的偏移量列表。它并非免费(即使仅用于保存每个偏移量的标量也需要占用每行一些空间),但通常足以轻松处理数百兆字节的文件。 - hobbs
@hobbs:是的。自那时以来(评论现在相当古老),我已经查看了文档,它很清楚地表明它不会占用太多内存。 - Zaid

0

你不需要进行分支。可以想象,从文件中读取特定行是一个常见的操作,CPAN 上的 20k 模块之一已经实现了它。

File::ReadBackwards 是内存高效且快速的。


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