Многие Perl-программисты имеют дело с текстовыми файлами, такими как конфиги или лог-файлы, поэтому, чтобы получить некоторые полезные знания, важно как можно раньше научиться работать с файлами.
Для начала давайте узнаем, как записывать данные в файл, так как это кажется наиболее простой задачей.
Прежде чем записывать в файл, нужно открыть его, то есть попросить операционную систему (Windows, Linux, OSX и т. д.) открыть канал, по которому ваша программа сможет "общаться" с файлом. Для этого в Perl есть функция
open
(с немного странным синтаксисом).use strict;
use warnings;
my $filename = 'отчет.txt';
open(my $fh, '>', $filename) or die "Не могу открыть '$filename' $!";
print $fh "Мой первый отчет, сгенерированный с помощью perl\n";
close $fh;
print "готово\n";
Это хороший рабочий пример и мы к нему еще вернемся, но сперва давайте попробуем пример попроще:
Простой пример
use strict;
use warnings;
open(my $fh, '>', 'отчет.txt');
print $fh "Мой первый отчет, сгенерированный с помощью perl\n";
close $fh;
print "готово\n";
Здесь тоже нужны кое-какие объяснения. Функция open принимает 3 параметра.
Первый, $fh
, это скалярная переменная, которую мы объясляем в вызове open()
.
Мы могли бы объявить ее раньше, но обычно проще объявить ее прямо в вызове,
хотя на первый взгляд это может выглядеть немного странно. Второй параметр определяет, каким
образом мы открываем файл.
В данном случае мы поставили знак "больше" (>
), что значит, что файл открывается
для записи.
Третий параметр - путь к файлу, который мы хотим открыть.
Когда эта функция вызывается, она присваивает переменной $fh
специальный ключ,
который называется указателем файла (file-handle). Нам не важно само содержимое этой
переменной; в дальнейшем мы просто используем ее. Главное, обратите внимание, что
содержимое файла по-прежнему находится на диске, и НЕ попадает в переменную $fh.
Когда файл открыт, мы можем использовать указатель $fh
в выражении print()
.
Это выглядит почти так же, как print()
в других частях нашего учебника,
но в качестве первого параметра мы передаем указатель файла, и после него нет(!) запятой.
Этот вызов print() запишет текст в наш файл.
Затем в следующей строчке мы закрываем указатель на файл. Строго говоря, в Perl это не обязательно. Perl автоматически и корректно закроет все файловые указатели, когда переменная покинет область видимости, то есть, в крайнем случае, когда скрипт завершится. Но, так или иначе, явно закрывать файлы считается хорошей практикой.
Последняя строчка print "done\n"
нужна только для того, чтобы пояснить следующий пример:
Обработка ошибок
Давайте возьмем предыдущий пример и заменим имя файла на несуществующий путь. Например:
open(my $fh, '>', 'некое_странное_название/отчет.txt');
Теперь, если запустить этот скрипт, мы увидим сообщение об ошибке:
print() on closed file-handle $fh at ...
готово
На самом деле, это просто предупреждение; скрипт продолжает выполняться, и поэтому мы увидим на экране слово "готово".
Более того, мы увидим предупреждение только потому, что явно запросили отображение предупреждений
с помощью выражения use warnings
.
Попробуем закомментировать use warnings
и увидим, что теперь скрипт молчит при
неудачной попытке открыть файл. Так что вы даже не заметите этого, пока клиент, или, хуже того,
ваш начальник, начнет жаловаться.
В любом случае, налицо проблема. Мы попытались открыть файл. Это не получилось, но мы все равно пытались туда что-то записать.
Лучше бы нам проверить, успешно ли сработал open()
, прежде чем продолжать.
К счастью, вызов open()
сам по себе возвращает
TRUE в случае успеха и FALSE при отказе,
так что мы можем сделать так:
Открой или умри (open or die)
open(my $fh, '>', 'некое_странное_название/отчет.txt') or die;
Это "стандартная идиома" open or die. Очень часто встречается в Perl.
die
- это вызов функции, которая бросит исключение и таким образом завершит наш скрипт.
"open or die" это логическое выражение. Как вы знаете из предыдущей части учебника, "or" в Perl (как и во многих других языках) сокращается. Это значит, что если левая часть вернет TRUE, сразу понятно, что все выражение будет равно TRUE, так что правая часть вообще не выполняется. С другой стороны, если левая часть вернет FALSE, то правая часть выполнится, и результат ее выполнения и будет результатом всего выражения.
В данном случае мы используем эту особенность сокращения в нашеи выражении.
Если open()
выполнится успешно, он вернет TRUE, и правая часть так и не выполнится.
Скрипт просто перейдет к следующей строчке.
Если же open()
не выполнится, он вернет FALSE. Тогда выражение справа от or
тоже
выполняется. Это приводит к исключению, и скрипт завершается.
В этом примере мы не проверяем итогового значения логического выражения, оно нам не нужно. Мы использовали это выражение только ради "побочного эффекта".
Если мы запустим скрипт с этим изменением, мы получим сообщение об ошибке:
Died at ...
и НЕ увидим "готово".
Улучшаем сообщение об ошибке
Вместо того, чтобы просто вызвать die без параметра, можно добавить некоторое объяснение того, что же произошло.
open(my $fh, '>', 'некое_странное_название/отчет.txt')
or die "Не могу открыть файл 'некое_странное_название/отчет.txt'";
выдаст
Не могу открыть файл 'некое_странное_название/отчет.txt' ...
Так-то лучше, но в какой-то момент кто-нибудь попробует поменять путь на корректную директорию...
open(my $fh, '>', 'корректная_директория_с_опечаткой/отчет.txt')
or die "Не могу открыть файл 'некое_странное_название/отчет.txt'";
...но сообщение об ошибке будет старым, потому что путь поменяли только в вызове open(), но не в сообщении.
Так что будет лучше использовать в качестве названия файла переменную:
my $filename = 'некое_странное_название/отчет.txt';
open(my $fh, '>', $filename) or die "Не могу открыть файл '$filename'";
Теперь у нас правильное сообщение об ошибке, но мы по-прежнему не знаем, почему она произошла.
Пойдем на шаг дальше, и используем $!
- встроенную переменную Perl - чтобы выдать то,
что нам сообщила система об ошибке:
my $filename = 'корректная_директория_с_опечаткой/отчет.txt';
open(my $fh, '>', $filename) or die "Не могу открыть файл '$filename' $!";
Этот код выдаст
Не могу открыть файл 'корректная_директория_с_опечаткой/отчет.txt' No such file or directory ...
Так гораздо лучше.
Ну а теперь вернемся к исходному примеру.
Больше?
Знак "больше" в вызове open может показаться непонятным, но если вы знакомы с перенаправлениями в командной строке, вы поймете, что он значит. А если нет, просто представьте, что это стрелка, показывающая направление потока данных: в файл справа.
Не латиница?
Если вам нужно работать с символами, не входящими в таблицу ASCII, вам стоит сохранять их в UTF-8. Чтобы это сделать, нужно сообщить Perl'у, что вы открываете файл в кодировке UTF-8.
open(my $fh, '>:encoding(UTF-8)', $filename)
or die "Не могу открыть файл '$filename'";