9

我正在编写一个 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 上运行吗?

4

11 回答 11

29

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

于 2008-11-19T19:58:31.460 回答
11

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

tail -1000 yourfile | perl ...

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

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

于 2008-11-19T19:32:31.187 回答
9

这仅与您的主要问题无关,但是当您想检查诸如File::Tail之类的模块是否在您的平台上工作时,请检查CPAN Testers的结果。CPAN 搜索模块页面顶部的链接将引导您到

文件尾标头
(来源:flickr.com

查看矩阵,您会发现在 Windows 上测试的所有 Perl 版本中,该模块确实存在问题:

文件尾矩阵
(来源:flickr.com

于 2008-11-20T03:24:31.170 回答
6

没有tail,仅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;
}

PS 一个更好的标题应该是“从 Perl 中的大文件末尾读取行”。

于 2008-11-19T19:53:25.747 回答
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
于 2012-08-18T09:03:49.560 回答
2
perl -n -e "shift @d if (@d >= 1000); push(@d, $_); END { print @d }" < bigfile.csv

虽然真的,UNIX 系统可以简单地tail -n 1000说服你简单地安装cygwincolinux

于 2008-11-19T19:50:32.367 回答
1

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

领带::文件

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

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

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

于 2008-11-19T19:37:26.450 回答
0

如果你知道文件中的行数,你可以这样做

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

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

perl -e "while (<>) {} print $.;" filename.csv
于 2008-11-19T19:47:14.933 回答
0

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

#!/usr/bin/perl

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

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

于 2012-10-30T11:30:06.577 回答
-1

如果你有超过 $FILESIZE [2GB?] 的内存,我可能会不依赖 tail ,那么我会很懒惰,做:

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

尽管涉及tail or的其他答案seek()几乎是解决这个问题的方法。

于 2008-11-19T20:42:08.457 回答
-1

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

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

于 2008-11-24T20:29:42.677 回答