Как прочитать строки из конца файла в Perl?

Я работаю над скриптом Perl для чтения CSV-файла и выполнения некоторых вычислений. CSV-файл имеет только два столбца, что-то вроде ниже.

One Two
1.00 44.000
3.00 55.000

теперь этот файл CSV очень большой, может быть от 10 МБ до 2 ГБ.

В настоящее время я беру CSV-файл размером 700 МБ. Я попытался открыть этот файл в notepad, excel, но похоже, что никакое программное обеспечение не собирается его открывать.

Я хочу прочитать последние 1000 строк из CSV-файла и посмотреть значения. Как я могу это сделать? Я не могу откройте файл в блокноте или любой другой программе.

Если я пишу сценарий Perl, мне нужно обработать полный файл, чтобы перейти к концу файла, а затем прочитать последние 1000 строк.

есть ли лучший способ сделать это? Я новичок в Perl и любые предложения будут оценены.

Я искал сеть, и есть некоторые скрипты, такие как File::Tail, но я не знаю, что они будут работать на windows ?

11 ответов


в *nix вы можете использовать команду tail.

tail -1000 yourfile | perl ...

это будет записывать только последние 1000 строк в программу perl.

на Windows, есть gnuwin32 и unxutils пакеты обоих есть tail утилиты.


на File:: ReadBackwards модуль позволяет читать файл в обратном порядке. Это позволяет легко получить последние N строк, Если вы не зависите от заказа. Если вы и необходимые данные достаточно малы (что должно быть в вашем случае), вы можете прочитать последние 1000 строк в массив, а затем reverse его.


Это только касательно связано с вашим основным вопросом, но когда вы хотите проверить, такой ли модуль, как File:: Tail работает на вашей платформе, проверить результаты тестеры CPAN. Ссылки в верхней части страницы модуля в поиск CPAN привести

file-tail-header

глядя на матрицу, вы видите, что действительно этот модуль имеет проблему в Windows на всех версиях Perl проверено:

file-tail-matrix


Я написал быстрый обратный поиск файлов, используя следующий код на pure 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

без хвоста решение Perl-only не является необоснованным.

один из способов-искать с конца файла, а затем читать строки из него. Если у вас недостаточно линий, ищите еще дальше от конца и повторите попытку.

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".


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

хотя на самом деле, тот факт, что системы UNIX могут просто tail -n 1000 должен убедить вас просто установить cygwin или colinux


вы можете использовать модуль Tie::File я считаю. Похоже, что это загружает строки в массив, тогда вы можете получить размер массива и обработать массивы-ze-1000 до arraySize-1.

Tie:: File

другой вариант-подсчитать количество строк в файле, затем выполнить цикл через файл один раз и начать чтение значений в numberofLines-1000

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

Это даст вам количество строк (в большинстве систем.


Если вы знаете количество строк в файле, вы можете сделать

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

где N - $num_lines_in_file - $num_lines_to_print. Вы можете считать строки с

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

модули-это путь. Однако иногда вы можете писать фрагмент кода, который хотите запустить на различных машинах, на которых могут отсутствовать более неясные модули CPAN. В этом случае почему бы просто не "хвост" и не сбросить вывод во временный файл из Perl?

#!/usr/bin/perl

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

у вас тогда есть что-то, что не зависит от модуля CPAN, если установка может представить проблему.


не полагаясь на хвост, что я, вероятно, сделал бы, если у вас больше $FILESIZE [2GB?] памяти тогда я бы просто ленился и делал:

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

хотя и другие ответы с участием tail или seek() в значительной степени способ пойти на это.


вы должны абсолютно использовать File:: Tail или, еще лучше, другой модуль. Это не скрипт, это модуль (библиотека Программирование). Вероятно, он работает на Windows. Как кто-то сказал, Вы можете проверить это на тестерах CPAN, или часто просто прочитав документацию модуля или просто попробовав его.

вы выбрали использование утилиты tail в качестве предпочтительного ответа, но это, вероятно, будет больше головной боли в Windows, чем File::Tail.