如何在Perl中读取文件末尾的行?

时间:2008-11-19 19:28:50

标签: perl large-files

我正在使用Perl脚本来读取CSV文件并进行一些计算。 CSV文件只有两列,如下所示。

One Two
1.00 44.000
3.00 55.000

现在这个CSV文件很大,可以从10 MB到2GB。

目前我正在使用大小为700 MB的CSV文件。我试着在记事本中打开这个文件,excel但看起来没有软件可以打开它。

我想阅读可能是CSV文件中的最后1000行并查看值。 我怎样才能做到这一点?我无法在记事本或任何其他程序中打开文件。

如果我编写一个Perl脚本,那么我需要处理完整的文件以转到文件末尾,然后读取最后1000行。

还有更好的方法吗?我是Perl的新手,任何建议都将不胜感激。

我搜索过网络,有一些脚本可用,比如File :: Tail,但我不知道它们会在windows上运行吗?

11 个答案:

答案 0 :(得分:27)

File::ReadBackwards模块允许您以相反的顺序读取文件。只要您不依赖于顺序,这就可以轻松获得最后N行。如果你是,并且所需的数据足够小(在你的情况下应该是这样),你可以将最后1000行读入数组,然后reverse

答案 1 :(得分:11)

在* nix中,您可以使用tail命令。

tail -1000 yourfile | perl ...

这只会将最后1000行写入perl程序。

在Windows上,gnuwin32unxutils个软件包都有tail实用程序。

答案 2 :(得分:9)

这与您的主要问题仅相关,但是当您想要检查File::Tail等模块是否适用于您的平台时,请检查CPAN Testers的结果。 CPAN Search中模块页面顶部的链接引导您

file-tail-header

查看矩阵,您会看到在所有Perl测试版本的Windows上确实存在此问题:

file-tail-matrix

答案 3 :(得分:5)

我在纯Perl上使用以下代码编写了快速后向文件搜索:

#!/usr/bin/perl 
use warnings;
use strict;
my ($file, $num_of_lines) = @ARGV;

my $count = 0;
my $filesize = -s $file; # filesize used to control reaching the start of file while reading it backward
my $offset = -2; # skip two last characters: \n and ^Z in the end of file

open F, $file or die "Can't read $file: $!\n";

while (abs($offset) < $filesize) {
    my $line = "";
    # we need to check the start of the file for seek in mode "2" 
    # as it continues to output data in revers order even when out of file range reached
    while (abs($offset) < $filesize) {
        seek F, $offset, 2;     # because of negative $offset & "2" - it will seek backward
        $offset -= 1;           # move back the counter
        my $char = getc F;
        last if $char eq "\n"; # catch the whole line if reached
        $line = $char . $line; # otherwise we have next character for current line
    }

    # got the next line!
    print $line, "\n";

    # exit the loop if we are done
    $count++;
    last if $count > $num_of_lines;
}

并运行此脚本,如:

$ get-x-lines-from-end.pl ./myhugefile.log 200

答案 4 :(得分:4)

没有尾巴,只有Perl的解决方案并不是那么不合理。

一种方法是从文件末尾搜索,然后从中读取行。如果您没有足够的线路,请从头到尾再寻找并再试一次。

sub last_x_lines {
    my ($filename, $lineswanted) = @_;
    my ($line, $filesize, $seekpos, $numread, @lines);

    open F, $filename or die "Can't read $filename: $!\n";

    $filesize = -s $filename;
    $seekpos = 50 * $lineswanted;
    $numread = 0;

    while ($numread < $lineswanted) {
        @lines = ();
        $numread = 0;
        seek(F, $filesize - $seekpos, 0);
        <F> if $seekpos < $filesize; # Discard probably fragmentary line
        while (defined($line = <F>)) {
            push @lines, $line;
            shift @lines if ++$numread > $lineswanted;
        }
        if ($numread < $lineswanted) {
            # We didn't get enough lines. Double the amount of space to read from next time.
            if ($seekpos >= $filesize) {
                die "There aren't even $lineswanted lines in $filename - I got $numread\n";
            }
            $seekpos *= 2;
            $seekpos = $filesize if $seekpos >= $filesize;
        }
    }
    close F;
    return @lines;
}

P.S。一个更好的标题就是“从Perl中的大文件末尾读取行”。

答案 5 :(得分:2)

perl -n -e "shift @d if (@d >= 1000); push(@d, $_); END { print @d }" < bigfile.csv

尽管如此,UNIX系统只能tail -n 1000这一事实应该说服您只需安装cygwincolinux

答案 6 :(得分:1)

我相信你可以使用Tie :: File模块。看起来这会将行加载到数组中,然后您可以获得数组的大小并将arrayS-ze-1000处理到arraySize-1。

Tie::File

另一种选择是计算文件中的行数,然后遍历文件一次,并开始读取numberofLines-1000的值

$count = `wc -l < $file`;
die "wc failed: $?" if $?;
chomp($count);

这会给你一些行数(在大多数系统上。

答案 7 :(得分:0)

如果您知道文件中的行数,则可以

perl -ne "print if ($. > N);" filename.csv

其中N是$ num_lines_in_file - $ num_lines_to_print。 您可以使用

计算行数
perl -e "while (<>) {} print $.;" filename.csv

答案 8 :(得分:0)

模块是要走的路。但是,有时您可能正在编写一段代码,您希望在各种可能缺少更加模糊的CPAN模块的机器上运行这些代码。那么为什么不只是'tail'并将输出转储到Perl中的临时文件?

#!/usr/bin/perl

`tail --lines=1000 /path/myfile.txt > tempfile.txt`

如果安装一个CPAN模块可能会出现问题,那么您有一些不依赖于CPAN模块的东西。

答案 9 :(得分:-1)

如果你的内存超过$ FILESIZE [2GB?],那么我可能不会依赖尾巴,那么我只是懒得去做:

my @lines = <>;
my @lastKlines = @lines[-1000,-1];

虽然涉及seek()的其他答案几乎可以解决这个问题。

答案 10 :(得分:-1)

你绝对应该使用File :: Tail,或者更好的另一个模块。它不是一个脚本,它是一个模块(编程库)。它可能适用于Windows。正如有人所说,您可以在CPAN测试仪上查看,或者通常只需阅读模块文档或尝试它。

您选择使用尾部实用程序作为首选答案,但在Windows上可能比File :: Tail更令人头疼。