Как я могу прочитать строки из конца файла в Perl? - PullRequest
7 голосов
/ 19 ноября 2008

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

One Two
1.00 44.000
3.00 55.000

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

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

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

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

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

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

Ответы [ 11 ]

27 голосов
/ 19 ноября 2008

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

11 голосов
/ 19 ноября 2008

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

tail -1000 yourfile | perl ...

Это запишет только последние 1000 строк в программу Perl.

В Windows есть пакеты gnuwin32 и unxutils , оба имеют утилиту tail.

9 голосов
/ 20 ноября 2008

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

file-tail-header

Looking at the matrix, you see that indeed this module has a problem on Windows on all version of Perl tested:

file-tail-matrix

5 голосов
/ 18 августа 2012

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

Без хвоста 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».

2 голосов
/ 19 ноября 2008
perl -n -e "shift @d if (@d >= 1000); push(@d, $_); END { print @d }" < bigfile.csv

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

1 голос
/ 19 ноября 2008

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

Tie :: Файл

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

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

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

0 голосов
/ 30 октября 2012

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

#!/usr/bin/perl

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

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

0 голосов
/ 24 ноября 2008

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

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

0 голосов
/ 19 ноября 2008

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

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

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

...