Обработка текстового файла построчно

Как Perl построчно читает файл, отбирает нужные строки по шаблону и превращает текстовый хаос в понятные данные.

Построчное чтение — способ обработки файла, при котором программа читает его не целиком, а по одной строке за раз, обрабатывает эту строку и переходит к следующей.

Зачем это нужно

Представь: на сервере лежит файл с логами — записями о том, кто и когда заходил на сайт. За месяц в нём миллионы строк. Тебе нужно найти только те, где были ошибки, и посчитать, сколько их было. Открывать файл в блокноте и листать глазами — не вариант, файл весит сотни мегабайт и просто не поместится на экране разумным образом.

Именно для таких задач и создавался Perl ещё в 1987 году. Его придумал системный администратор Ларри Уолл, которому надоело писать отчёты вручную. Он хотел язык, который умеет быстро прочитать текст, найти в нём закономерность и что-то с этим сделать. Спустя почти сорок лет Perl всё ещё остаётся одним из лучших инструментов именно для этой работы — обработки логов, конфигов, CSV-файлов и вообще любого текста.

Открываем файл и читаем построчно

Чтобы поработать с файлом, в Perl сначала нужно его открыть — связать имя файла на диске с переменной в программе, которую называют файловым дескриптором. Затем можно читать из него по одной строке.

open(my $fh, '<', 'server.log') or die "Не могу открыть файл: $!";

while (my $line = <$fh>) {
    print $line;
}

close($fh);

Вывод:

2026-06-30 09:12:01 INFO  Пользователь user42 вошёл в систему
2026-06-30 09:12:15 ERROR Не удалось подключиться к базе данных
2026-06-30 09:13:02 INFO  Пользователь user17 вышел из системы
...

Разберём построчно, что здесь происходит:

  • open(my $fh, '<', 'server.log') — открывает файл server.log на чтение. Знак '<' — это как раз указание «читать», а не «писать». Результат сохраняется в переменную $fh (от file handle, «файловый дескриптор»).
  • or die "..." — если файл не открылся (например, его не существует), программа немедленно останавливается и печатает сообщение об ошибке. $! — специальная переменная Perl, в которой лежит текст последней системной ошибки.
  • while (my $line = <$fh>) — сердце конструкции. Символы <$fh> означают «прочитать одну строку из дескриптора $fh». Каждый раз, когда цикл while проверяет условие, Perl читает следующую строку и кладёт её в $line. Когда строки заканчиваются, <$fh> возвращает undef (пусто), и цикл завершается сам — не нужно отдельно проверять конец файла.
  • close($fh) — закрывает файл, когда работа с ним закончена. Хорошая привычка, хоть Perl и закроет файл сам при завершении программы.

Отбираем строки по шаблону

Просто печатать файл целиком скучно — куда полезнее найти конкретные строки. Для этого Perl использует регулярные выражения — язык для описания текстовых шаблонов, встроенный прямо в синтаксис Perl (это одна из его суперспособностей). Оператор =~ проверяет, соответствует ли строка шаблону.

open(my $fh, '<', 'server.log') or die "Не могу открыть файл: $!";

my $error_count = 0;

while (my $line = <$fh>) {
    if ($line =~ /ERROR/) {
        print $line;
        $error_count++;
    }
}

close($fh);
print "Всего ошибок: $error_count\n";

Вывод:

2026-06-30 09:12:15 ERROR Не удалось подключиться к базе данных
2026-06-30 09:47:33 ERROR Таймаут запроса к API
Всего ошибок: 2

Здесь /ERROR/ — это и есть регулярное выражение, записанное между двумя слэшами. В простейшем виде это просто кусок текста, который Perl ищет внутри строки. Если слово ERROR где-то встретилось — условие истинно, строка печатается, а счётчик $error_count увеличивается на единицу оператором ++.

Вытаскиваем данные из строки

Найти строку — половина дела. Часто нужно достать из неё конкретный кусок: например, только время события. Для этого шаблон дополняют группами захвата — частями в круглых скобках, которые Perl запоминает отдельно.

open(my $fh, '<', 'server.log') or die "Не могу открыть файл: $!";

while (my $line = <$fh>) {
    if ($line =~ /^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) ERROR (.+)/) {
        print "Время: $1, ошибка: $2\n";
    }
}

close($fh);

Вывод:

Время: 2026-06-30 09:12:15, ошибка: Не удалось подключиться к базе данных
Время: 2026-06-30 09:47:33, ошибка: Таймаут запроса к API

Шаблон выглядит пугающе, но по частям всё понятно:

  • ^ — «строка должна начинаться с этого места» (иначе ERROR могло бы найтись где угодно посреди строки).
  • \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\d означает «любая цифра», а {4} — «ровно четыре раза». То есть это описание даты и времени вида 2026-06-30 09:12:15.
  • Круглые скобки ( ... ) вокруг даты — первая группа захвата. То, что она «поймает», попадёт в специальную переменную $1.
  • (.+) — вторая группа захвата. Точка . значит «любой символ», плюс + — «один или больше раз». То есть «захватить весь оставшийся текст». Он попадёт в переменную $2.

После успешного совпадения Perl автоматически заполняет переменные $1, $2 и так далее — по числу групп в шаблоне, слева направо. Это очень удобно: не нужно вручную вырезать подстроки, достаточно один раз описать форму данных.

Частые ошибки

  • Забыть проверить, открылся ли файл. Без or die программа при отсутствующем файле просто тихо ничего не сделает, и разобраться, что пошло не так, будет сложнее.
  • Перепутать =~ с обычным присваиванием =. =~ — это «проверить строку на соответствие шаблону», а не «присвоить значение». Одна лишняя тильда — и программа не скомпилируется.
  • Забыть про перенос строки в конце. Каждая строка, прочитанная через <$fh>, обычно содержит символ конца строки \n на конце. Если вывести такую строку без изменений вперемешку с другим текстом, можно получить лишние переносы. Убрать его помогает функция chomp.
  • Слишком жадный шаблон. Символ .+ захватывает как можно больше символов, и иногда «проглатывает» не то, что задумывалось. В сложных случаях стоит делать шаблон точнее, а не полагаться на то, что «плюс возьмёт всё нужное».

Итоги

  • Файл открывают через open(my $fh, '<', 'имя_файла') и читают по одной строке циклом while (my $line = <$fh>).
  • Оператор =~ проверяет строку на соответствие регулярному выражению — шаблону, который описывает, какой текст мы ищем.
  • Круглые скобки в шаблоне создают группы захвата, значения которых после совпадения оказываются в переменных $1, $2 и так далее.
  • Такой способ обработки — основа огромного числа реальных задач: анализ логов, поиск ошибок, сбор статистики из текстовых файлов.
Проверьте себя
1. Что делает конструкция while (my $line = <$fh>) в Perl?
AЧитает файл целиком в одну переменную $line
BЧитает файл по одной строке за итерацию, пока строки не закончатся
CУдаляет файл после чтения
DЗаписывает переменную $line в файл
2. В шаблоне /^(\d{4}-\d{2}-\d{2}) ERROR/ после успешного совпадения — где окажется найденная дата?
AВ переменной $line без изменений
BВ переменной $1, потому что дата взята в круглые скобки
CВ переменной $ERROR
DДата нигде не сохраняется, только проверяется совпадение