Доступ к файлам
Введение
Файлы занимают центральное место в обработке данных. Как и во всем остальном в Perl, простые операции с файлами выполняются просто, а сложные... как-нибудь да выполняются. Стандартные задачи (открытие файлов, чтение данных, запись данных) используют прос
В этой главе рассматривается механика доступа к файлам: открытие файлов, передача сведений о том, с какими файлами вы собираетесь работать, блокировка и т. д. Глава 8 «Содержимое файлов» посвящена работе с содержимым файлов: чтению, записи, перестановке
Следующий фрагмент выводит все строки файла /usr/local/widgets/data, содержащие слово "blue":
open (INPUT, "< /usr/local/widgets/data")
or die "Couldn't open /usr/local/widgets/data for reading: $!\n";
while (INPUT) {
print if /blue/, } close(INPUT);
Получение файлового манипулятора
Доступ к файлам в Perl организуется при помощи файловых манипуляторов (filehandle) - таких, как INPUT из предыдущего примера. Манипулятор - это символическое имя, которое представляет файл в операциях чтения/записи. Файло-
вые манипуляторы не являются переменными. В их именах отсутствуют префиксы $, @ или %, однако они наряду с функциями и переменными попадают в символьную таблицу Perl. По этой причине не всегда удается сохранить файловый манипулятор в переменной или пере
$var = *STDIN;
my sub($var, *LOGFILE);
Файловые манипуляторы, сохраняемые в переменных подобным образом, не используются напрямую. Они называются косвенными файловыми манипуляторами (indirect filehandle), поскольку косвенно ссылаются на настоящие манипуляторы. Два модуля, IO::File (стал стан
Когда в наших примерах используются модули IO::File или IO::Handle, аналогичные результаты можно получить с применением модуля FileHandle, поскольку сейчас он является интерфейсным модулем (wrapper).
Ниже показано, как выглядит программа для поиска "blue" с применением модуля IO::File в чисто объектной записи:
use 10::File:
$input = 10::File->new("< /usr/local/widgets/data")
or die "Couldn't open /usr/local/widgets/data for reading: $!\n":
while (defined($line = $input->getline())) {
chomp($line);
STDOUT->print($line) if $line =~ /blue/;
} $input->close();
Как видите, без прямого использования файловых манипуляторов программа читается намного легче. Кроме того, она гораздо быстрее работает.
Но поделимся одним секретом: из этой программы можно выкинуть все стрелки и вызовы методов. В отличие от большинства объектов, объекты IO::File не обязательно использовать объектно-ориентированным способом. В сущности, они представляют собой анонимные фа
Стандартные файловые манипуляторы
Каждая программа при запуске получает три открытых глобальных файловых манипулятора: STDIN, STDOUT и STDERR. STDIN {стандартный ввод) является источником входных данных по умолчанию. В STDOUT {стандартный вывод) по умолчанию направляются выходные данные
активных программах STDIN соответствует клавиатуре, a STDOUT и STDERR - экрану монитора:
while() { # Чтение из STDIN
unless (/\d/) {
warn "No digit found.\n"; # Вывод в STDERR
}
print "Read: ", $_; # Запись в STDOUT
} END { close(STDOUT) or die "couldn't close STDOUT: $!" }
Файловые манипуляторы существуют на уровне пакетов. Это позволяет двум пакетам иметь разные файловые манипуляторы с одинаковыми именами (по аналогии с функциями и переменными). Функция open связывает 4)айловый манипулятор с файлом или программой, после
Операционная система работает с файлами через файловые дескрипторы, значение которых определяется функцией fileno. Для большинства файловых операций хватает манипуляторов Perl, однако в рецепте 7.19 показано, как файловый дескриптор преобразуется в файл
Операции ввода/вывода
Основные функции для работы с файлами в Perl - open, print, <...> (чтение записи) и close. Они представляют собой интерфейсные функции для процедур буферизованной библиотеки ввода/вывода С stdio. Функции ввода/вывода Perl документированы в perlfunc( 1) и
Важнейшей .функцией ввода/вывода является функция open. Она получает два аргумента - файловый манипулятор и строку с именем файла и режимом доступа. Например, открытие файла /tmp/log для записи и его связывание с манипулятором LOG FILE выполняется следу
open(LOGFILE, "> /tmp/log") or die "Can't write /tmp/log: $!";
Три основных режима доступа - < (чтение), > (запись) и » (добавление). Дополнительные сведения о функции open приведены в рецепте 7.1.
При открытии файла или вызове практически любой системной функции* необходимо проверять возвращаемое значение. Не каждый вызов open заканчивается успешно; не каждый файл удается прочитать; не каждый фрагмент данных, выводимый функцией print, достигает
Системной функцией называется обращение к сервису операционной системы. Термин не имеет отношения к функции system в языках С и Perl.
syscall, возвращающих -1). Системное сообщение или код ошибки хранится в переменной $1 и часто используется в die или сообщениях warn.
Для чтения записей в Perl применяется оператор <МАНИПУЛЯТОР>, также часто дублируемый функцией readllne. Обычно запись представляет собой одну строку, однако разделитель записей можно изменить (см. главу 8). Если МАНИПУЛЯТОР не указывается, Perl открывае
С абстрактной точки зрения файл представляет собой обычный поток байтов. Каждый файловый манипулятор ассоциируется с числом, определяющим текущую позицию внутри файла. Текущая позиция возвращается функцией tell и устанавливается функцией seek. В рецепте
Когда надобность в файловом манипуляторе отпадает, закройте его функцией close. Функция получает один аргумент (файловый манипулятор) и возвращает true, если буфер был успешно очищен, а файл - закрыт, и false в противном случае. Закрывать все манипулятор
Неявное закрытие файлов реализовано для удобства, а не для повышения надежности, поскольку вы не узнаете, успешно ли завершилась системная функция. Не все попытки закрытия завершаются успешно. Даже если файл открыт только для чтения, вызов close может за
close(FH) or die "FH didn't close: $!";
Усердный программист даже проверяет результат вызова close для STDOUT в конце программы на случай, если выходные данные были перенаправлены в командной строке, а выходная файловая система оказалась переполнена. Вообще-то об этом должна заботиться runtim
Впрочем, проверка STDERR выглядит сомнительно. Даже если этот поток не закроется, как вы собираетесь на это реагировать?
Манипулятор STDOUT по умолчанию используется для вывода данных функциями print, printf и write. Его можно заменить функцией select, которая получает новый и возвращает предыдущий выходной манипулятор, используемый по умолчанию. Перед вызовом select долж
$old_fh = select(LOGFILE); # Переключить вывод на LOGFILE print "Countdown initiated ...\n";
select($old_fh); # Вернуться к выводу на прежний манипулятор
print "You have 30 seconds to reach minumum safety distance.\n";
Некоторые специальные переменные Perl изменяют поведение текущего файлового манипулятора вывода. Особенно важна переменная $ |, которая управляет
буферизацией вывода для файловых манипуляторов. Буферизация рассматривается в рецепте 7.12.
Функции ввода/вывода в Perl делятся на буферизованные и небуферизованные (табл. 7.1). Несмотря на отдельные исключения, не следует чередовать их вызовы в программе. Связь между функциями, находящимися в одной строке таблицы, весьма условна. Например, но с
Таблица 7.1 Функции ввода/вывода в Perl Действие Буферизованные функции Небуферизованные функции
Открытие open,sysopen sysopen Закрытие close close Ввод <...>, readline sysread Вывод print syswrite Позиционирование seek, tell_
Позиционирование рассматривается в главе 8, однако мы также воспользуемся им в рецепте 7.10.
Проблема
Известно имя файла. Требуется открыть его для чтения или записи в Perl.
Решение
Функция open отличается удобством, sysopen - точностью, а модуль IO::File позволяет работать с анонимным файловым манипулятором.
Функция open получает два аргумента: открываемый файловый манипулятор и строку с именем файла и специальными символами, определяющими режим открытия:
open(SOURCE, "< $path")
or die "Couldn't open $path for reading: $!\n";
open(SINK, "> $path")
or die "Couldn't open $path for writing: $!\n";
где SOURCE - файловый манипулятор для ввода, a SINK - для вывода.
Функции sysopen передаются три или четыре аргумента: файловый манипулятор, имя файла, режим и необязательный параметр, определяющий права доступа. Режим представляет собой число, конструируемое из констант модуля Fcnti:
use Fcnti;
sysopen(SOURCE, $path, O.RDONLY)
or die "Couldn't open $path for reading: $!\n";
sysopen(SINK, $path, 0_WRONLY)
or die "Couldn't open $path for writing: $!\n";
Аргументы метода new модуля IO::File могут задаваться в стиле как open, так и sysopen. Метод возвращает анонимный файловый манипулятор. Кроме того, также возможно задание режима открытия в стиле fopen(3):
use 10::File;
# По аналогии с
open $sink = 10::File->new("> $filename")
or die "Couldn't open $filename for writing: $!\n";
# По аналогии с sysopen
$fh = 10::File->new($filename, 0_WRONLY|0_CREAT)
or die "Couldn't open $filename for reading: $!\n";
# По аналогии с fopen(3) библиотеки
stdio $fh = 10::File->new($filename, "r+")
or die "Couldn't open $filename for read and write: $!\n";
Комментарий
Все операции ввода/вывода осуществляются через файловые манипуляторы независимо от того, упоминаются манипуляторы в программе или нет. Файловые манипуляторы не всегда связаны с конкретными файлами - они также применяются для взаимодействия с другими прог
Функция open позволяет быстро и удобно связать файловый манипулятор с файлом. Вместе с именем файла передаются сокращенные обозначения стандартных режимов (чтение, запись, чтение/запись, присоединение). Функция не позволяет задать права доступа для созд
Большинство программистов начинает работать с open задолго до первого использования sysopen. В таблице показано соответствие между режимами функции open («Файл»), константами sysopen («Флаги») и строками fopen(3), передаваемыми 10: :File->new («Символы
7.1.
Открытие файла 245
|
Файл
|
Чтение
|
Запись
|
Присое
|
Созда
|
Очистка
|
Флаги
|
Символы
|
|
|
|
динение
|
ние
|
содержи
|
0
|
|
|
|
|
|
|
мого
|
|
|
•
< файл
|
Да
|
Нет
|
Нет
|
Нет
|
Нет
|
RDONLY
|
"г"
|
>
файл,
|
Нет
|
Да
|
Нет
|
Да
|
Да
|
WRONLY
|
"W"
|
режим
|
|
|
|
|
|
TRUNC
|
|
открытия>
|
|
|
|
|
|
CREAT
|
|
»
файл>,
|
Нет
|
Да
|
Да
|
Да
|
Нет
|
WRONLY
|
"а"
|
режим
|
|
|
|
|
|
APPEND
|
|
открытня>
|
|
|
|
|
|
CREAT
|
|
+<
файл
|
Да
|
Да
|
Нет
|
Нет
|
Нет
|
RDWR-
|
"г+"
|
+>
файл,
|
Да
|
Да
|
Нет
|
Да
|
Да
|
RDWR
|
"W+"
|
режим
|
|
|
|
|
|
TRUNC
|
|
открытия>
|
|
|
|
|
|
CREAT
|
|
+»
файл>,
|
Да
|
Да
|
Да
|
Да
|
Нет
|
RDWR
|
"а+"
|
режим
|
|
|
|
|
|
APPEND
|
|
открытия>
|
|
|
|
|
|
CREAT
|
|
Подсказка: режимы +> и +» почти никогда не используются. В первом случае файл уничтожается еще до того, как он будет прочитан, а во втором часто возникают затруднения, связанные с тем, что указатель чтения может находиться в произвольной позиции, но
Функция sysopen получает три или четыре аргумента:
sysopen(FILEHANDLE, sysopen(FILEHANDLE,
$name, $flags) or die "Can't open $name : $! "; $name, $Hags, Sperms) or die "Can't open $name : $!";
Здесь $name - имя файла без «довесков» в виде < или +; $flags - число, полученное объединением констант режимов 0_creat, 0_wronly, 0_trunc и т. д. операцией or. Конкретный состав доступных констант 0_ зависит от операционной системы. Дополнительные све
0_RDONLY Только чтение.
0_WRONLY Только запись.
0_RDWR Чтение и запись.
0_CREAT Создание файла, если он не существует.
0_EXCL Неудачное завершение, если файл уже существует.
0_APPEND Присоединение к файлу.
0_TRUNC Очистка содержимого файла.
0_NONBLOCK Асинхронный доступ.
К числу менее распространенных констант принадлежат 0_SHLOCK, 0_EXLOCK, 0_BINARY, 0_NOCTTY и 0_SYNC. Обращайтесь к странице руководства open (2) или к ее эквиваленту.
Если функции sysopen не передается аргумент $perms, Perl использует восьмеричное число 0666. Права доступа задаются в восьмеричной системе и учитывают текущее значение маски доступа (задаваемой функцией umask) процесса. В маске доступа сброшенные биты с
Если у вас возникнут затруднения с масками доступа, воспользуйтесь простым советом: передавайте значение 0666 для обычных файлов и 0777 для каталогов и исполняемых файлов. У пользователя появляется выбор: если ему понадобятся защищенные файлы, то может
Приведем примеры практического использования open и sysopen.
Открытие файла для чтения:
open(FH, "< $path") or die$!;
sysopen(FH, $path, 0_RDONLY) or die$!;
Открытие файла для записи (если файл не существует, он создается, а если существует - усекается):
open(FH, "> $path") or die$!;
sysopen(FH, $path, 0_WRONLY|0_TRUNC|0_CREAT) or die$!;
sysopen(FH, $path, 0_WRONLY|0_TRUNC|0_CREAT, 0600) or die$!;
Открытие файла для записи с созданием нового файла (файл не должен существовать):
sysopen(FH, $path, 0_WRONLY|0_EXCL|0_CREAT) or die$!;
sysopen(FH, $path, 0_WRONLY|0_EXCL|0_CREAT, 0600) or die$!:
Открытие файла для присоединения (в случае необходимости файл создается):
open(FH, "» $path") or die$!; sysopen(FH, $path, 0_WRONLY|0_APPEND|0_CREAT) or die$!; sysopen(FH, $path, 0_WRONLY|0_APPEND|0_CREAT, 0600) or die$!;
Открытие файла для присоединения (файл должен существовать):
sysopen(FH, $path, 0_WRONLY|0_APPEND) or die$!;
Открытие файла для обновления (файл должен существовать):
open(FH, "+< $path") or die$!;
sysopen(FH, $path, 0_RDWR) or die$!;
Открытие файла для обновления (в случае необходимости файл создается):
sysopen(FH, $path, 0_RDWR|0_CREAT) or die$!;
sysopen(FH, $path, 0_RDWR|0_CREAT, 0600) or die$!;
Открытие файла для обновления (файл не должен существовать):
sysopen(FH, $path, 0_RDWR|0_EXCL|0_CREAT) or die$!;
sysopen(FH, $path, 0_RDWR|0_EXCL|0_CREAT, 0600) or die$!;
Маска 0600 всего лишь поясняет, как создаются файлы с ограниченным доступом. Обычно этот аргумент пропускается.
Смотри также --------------------------------
Описание функций open, sysopen и umask в perlfunc(1); документация по стандартным модулям IO::File и Fcnti; страницы руководства open(2), fopen(3) и umask(2); рецепт 7.2.
Проблема
Требуется открыть файл с нестандартным именем - например, "-"; начинающимся с символа <, > или |; содержащим начальные или конечные пропуски; закапчивающимся символом |. Функция open не должна принимать эти функции за служебные, поскольку вам нужно сов
Решение
Выполните предварительное преобразование:
$filename =~ s#"(\s)#./$1#;
open(HANDLE, "< $filename\0") or die "cannot open $filename : $!\n":
Или просто воспользуйтесь функцией sysopen:
sysopen(HANDLE, $filename, 0_RDONLY) or die "cannot open $filename : $!\n";
Комментарий
Функция open определяет имя файла и режим открытия по одному строковому аргументу. Если имя файла начинается с символа, обозначающего один из режимов, open вполне может сделать что-нибудь неожиданное. Рассмотрим следующий фрагмент:
$filename = shift @ARGV;
open(INPUT, $filename) or die "cannot open $filename : $!\n":
Если пользователь указывает в командной строке файл ">/etc/passwd", программа попытается открыть /etc/passwd для записи - со всеми вытекающими последствиями! Режим можно задать и явно (например, для записи):
open(OUTPUT, ">$filename")
or die "Couldn't open $filename for writing: $!\n";
но даже в этом случае пользователь может ввести имя ">data", после чего программа будет дописывать данные в конец файла data вместо того, чтобы стереть прежнее содержимое.
Самое простое решение - воспользоваться функцией sysopen, у которой режим и имя файла передаются в разных аргументах:
use Fcnti; # Для файловых констант
sysopen(OUTPUT, $filename, 0_WRONLY|0_TRUNC)
or die "Couldn't open $filename for writing: $!\n";
А вот как добиться того же эффекта с функцией open для имен файлов, содержащих начальные или конечные пропуски:
$file =~ зГ(\з)#./$1#;
open(HANDLE, "> $file\0")
or die "Could't open $file for OUTPUT : $!\n";
Такая подстановка защищает исходные пропуски, но не в абсолютных именах типа " /etc/passwd", а лишь в относительных (" passwd"). Функция open не считает нуль-байт ("\0") частью имени файла, но благодаря ему не игнорируются конечные пропуски.
Волшебная интерпретация файловых имен в функции open почти всегда оказывается удобной. Вам никогда не приходится обозначать ввод или вывод с помощью особой формы "-". Если написать фильтр и воспользоваться простой функцией open, пользователь сможет пер
Вопросы безопасности open актуальны лишь для программ, работающих в особых условиях. Если программа должна работать под управлением чего-то другого - например, сценариев CGI или со сменой идентификатора пользователя, - добросовестный программист всегда
> Смотри также -------------------------------
Описание 4)ункций open и sysopen в perlfunc(1); рецепты 7.1, 7.7, 16.2, 19.4 и 19.6.
Проблема
Имя файла начинается с тильды (например, -usemame/blah), однако функция open не интерпретирует его как обозначение домашнего каталога (home directory).
Решение
Выполните ручное расширение с помощью следующей подстановки:
$filename =- s{ ~ ~ ( ["/]* ) } { $1
? (getpwnam($1))[7] : ( $ENV{HOME} || $ENV{LOGDIR}
| (getpwuid($>))[7] ) }ех;
Комментарий
Нас интересуют следующие применения тильды:
-user
-user/blah
-/blah
где user - имя пользователя.
Если ~ не сопровождается никаким именем, используется домашний каталог текущего пользователя.
В данной подстановке использован параметр /е, чтобы заменяющее выражение интерпретировалось как программный код Perl. Если за тильдой указано имя пользователя, оно сохраняется в $1 и используется getpwnam для выбора домашнего каталога пользователя из воз
> Смотри также
Описание функции getpwnam в perlfunc(1); man-страница getpwnam(2) вашей системы; рецепт 9.6.
Проблема
Программа работает с файлами, однако в предупреждения и сообщения об ошибках Perl включается только последний использованный файловый манипулятор, а не имя файла.
Решение
Воспользуйтесь именем файла вместо манипулятора:
open($path, "< $path")
or die "Couldn't open $path for reading : $!\n";
Комментарий
Стандартное сообщение об ошибке выглядит так:
Argument "3\n" isn't numeric in multiply at tallyweb line 16, chunk 17.
Манипулятор LOG не несет полезной информации, поскольку вы не знаете, с каким файлом он был связан. Если файловый манипулятор косвенно передается через имя файла, предупреждения и сообщения об ошибках Perl становятся более содержательными:
Argument "3\n" isn't numeric in multiply at tallyweb line 16, chunk 17.
К сожалению, этот вариант не работает при включенной директиве strict refs, поскольку переменная $path в действительности содержит не файловый манипулятор, а всего лишь строку, которая иногда ведет себя как манипулятор. Фрагмент (chunk), упоминаемый в п
> Смотри также -------------------------------
Описание функции open в perlfunc(1); рецепт 7.1.
Проблема
Требуется создать временный файл и автоматически удалить его при завершении программы. Допустим, вы хотите записать временный конфигурационный файл, который будет передаваться запускаемой программе. Его имя должно быть известно заранее. В других ситуация
Решение
Если имя файла не существенно, воспользуйтесь методом класса new_tmpfile модуля IO::File для получения файлового манипулятора, открытого для чтения и записи:
use 10::File;
$fh = 10::File->new_tmpfile
or die "Unable to make new temporary file: $!";
Если имя файла должно быть известно, получите его функцией tmpnam из модуля POSIX и откройте файл самостоятельно:
use 10::File;
use POSIX qw(tmpnam);
# Пытаться получить временное имя файла до тех пор,
# пока не будет найдено несуществующее имя
do { $name = tmpnam() }
until $fh = 10::File->new($name, 0_ROWR|0_CREAT|0_EXCL);
# Установить обработчик, который удаляет временный файл tt при нормальном или аварийном завершении программы
END { unlink($name) or die "Couldn't unlink $name : $!" } # Перейти к использованию файла....
Комментарий
Если все, что вам нужно, - область для временного хранения данных, воспользуйтесь методом new_tmpfile модуля IO::File. Он возвращает файловый манипулятор для временного файла, открытого в режиме чтения/записи фрагментом следующего вида:
for (;;) {
$name = tmpnam();
sysopen(TMP, $tmpnam, 0_RDWR | 0_CREAT | 0_EXC) && last;
} unlink $tmpnam;
Файл автоматически удаляется при нормальном или аварийном завершении программы. Вам не удастся определить имя файла и передать другому процессу, потому что у него нет имени. В системах с поддержкой подобной семантики имя удаляется еще до завершения метода
Ниже показан пример практического применения new_tmpfile. Мы создаем временный файл, выполняем запись, возвращаемся к началу и выводим записанные данные:
use 10::File;
$fh = 10::File->new_tmpfile or die "10::File->new_tmpfile: $!";
$fh->autorlush(1);
print( $fh "$i\n" while $i++ < 10;
seek($fh, 0, 0);
print "Tmp file has: ", <$rh>;
Во втором варианте создается временный файл, имя которого можно передать другому процессу. Мы вызываем функцию POSIX: :tmpnam, самостоятельно открываем файл и удаляем его после завершения работы. Перед открытием файла мы не проверяем, существует ли файл
> Смотри также --------------------------------
Документация по стандартным модулям IO::File и POSIX; рецепт 7.19; страница руководства tmpnam{3) вашей системы.
Но перед вызовом ехес следует присвоить УР хотя бы fileno($fh). 2 См. рецепт 19.4.
Проблема
Некоторые данные должны распространяться вместе с программой и интерпретироваться как файл, но при этом они не должны находиться в отдельном файле.
Решение
Лексемы __DATA__ и __END__ после исходного текста программы отмечают начало блока данных, который может быть прочитан программой или модулем через файловый манипулятор DATA.
В модулях используется лексема __DATA__:
while () {
# Обработать строку }
„DATA__ и Данные
Аналогично используется __END__ в главном файле программы:
while () {
# Обработать строку
}
-END__
# Данные
Комментарий
Лексемы __DATA__ и __END__ обозначают логическое завершение модуля или сценария перед физическим концом файла. Текст, находящийся после __ОАТА__ или __END__, может быть прочитан через файловый манипулятор DATA уровня пакета. Предположим, у нас имеется ги
Лексема __END__ представляет собой синоним __DATA__ в главном пакете. Текст, следующий после лексем __END__ в модулях, недоступен.
Появляется возможность отказаться от хранения данных в отдельном файле и перейти к построению автономных программ. Такая возможность нередко используется для документирования. Иногда в программах хранятся конфигурационные или старые тестовые данные, исп
Манипулятор DATA также применяется для определения размера или даты последней модификации текущей программы или модуля. В большинстве систем переменная $0 содержит полное имя файла для работающего сценария. В тех системах, где значение $0 оказывается не
use POSIX qw(strftime);
$raw_time = (stat(DATA))[9];
$size = -s DATA;
$kilosize = int($size / 1024) . 'k';
print "Script size is $kilosize\n";
print strftime("
Last script update: %c (%Z)\n", localtime($raw_time));
__DATA__
DO NOT REMOVE THE PRECEDING LINE
Everything else in this file will be ignored.
> Смотри также ---------------------------
Раздел «Scalar Value Constructors» perldata(1).
Используются технологии
uCoz