Файл представляет собой последовательность байтов без понятия «строки». Некоторые из этих байтов рассматриваются как разделители строк (перевод строки), и именно так программное обеспечение дает нам возможность работать с нашими «логическими» строками. Таким образом, невозможно узнать, сколько строк в файле - не прочитав и не посчитав их, то есть.
Простой и наивный способ - читать построчно и считать
open my $fh, '<', $file or die "Can't open $file: $!";
my $cnt;
++$cnt while <$fh>;
с немного более быстрой версией с использованием $.
переменная
1 while <$fh>;
my $cnt = $.;
Это занимает от 2,5 до 3 секунд для текстового файла объемом 1,1 Гб на приемлемом рабочем столе.
Мы можем значительно ускорить это, читая большие куски и считая символы новой строки
open my $fh, '<', $file or die "Can't open $file: $!";
my $cnt;
NUM_LINES: {
my $len = 64_000;
my $buf;
$cnt += $buf =~ tr/\n//
while read $fh, $buf, $len;
seek $fh, 0, 0;
};
Это занимает чуть более полсекунды, на том же оборудовании и версиях Perl.
Я поместил его в блок, чтобы охватить ненужные переменные, но он должен быть в подпрограмме, где вы можете проверить, где находится дескриптор файла, когда вы его получите, и вернуть его после подсчета (чтобы мы могли посчитать«остаток» строк из некоторой точки в файле, после чего обработка может быть продолжена) и т. д. Он также должен включать проверки операции read
при каждом вызове.
Я бы подумал, что полсекунды на больших файлах Gb совсем не так уж и плохо.
Тем не менее, вы можете пойти еще быстрее, за счет того, что это намного грязнее. Получите размер файла (метаданные, поэтому чтение не требуется) и seek
в положение, которое оценивается как требуемое количество строк до конца (чтение не требуется). Это, скорее всего, не попадет в нужное место, поэтому прочитайте до конца, чтобы пересчитать строки и скорректировать, ища назад (дальше или ближе). Повторяйте до тех пор, пока не достигнете нужного места.
open my $fh, "<", $file;
my $size = -s $file;
my $estimated_line_len = 80;
my $num_last_lines = 100;
my $pos = $size - $num_last_lines*$estimated_line_len;
seek $fh, $pos, 0;
my $cnt;
++$cnt while <$fh>;
say "There are $cnt lines from position $pos to the end";
# likely need to seek back further/closer ...
Я предполагаю, что это должно привести вас туда менее чем за 100 мс. Обратите внимание, что $pos
, скорее всего, находится внутри строки.
Затем, как только вы узнаете количество строк (или позицию для нужного количества строк до конца), выполните seek $fh, 0, 0
и обработайте. Или действительно иметь это в подпрограмме, которая возвращает дескриптор файла туда, где он был до возврата, как уже упоминалось.