Приглашаем посетить
Достоевский (dostoevskiy-lit.ru)

Доступ к файлам

Назад
Глава 7 Доступ к файлам
Вперед

Доступ к файлам

Введение

Файлы занимают центральное место в обработке данных. Как и во всем осталь-ном в Perl, простые операции с файлами выполняются просто, а сложные... как-нибудь да выполняются. Стандартные задачи (открытие файлов, чтение данных, запись данных) используют простые функции ввода/вывода и операторы, а бо-лее экзотические функции способны даже на асинхронный ввод/вывод и блоки-ровку (locking) файлов. В этой главе рассматривается механика доступа к файлам: открытие файлов, передача сведений о том, с какими файлами вы собираетесь работать, блоки-ровка и т. д. Глава 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. По этой причине не всегда удается сохранить файловый манипулятор в переменной или передать его функции. Приходится использовать префикс *, который является признаком тип-глоба - базовой единицы символь-ной таблицы Perl:
$var = *STDIN;
my sub($var, *LOGFILE);
Файловые манипуляторы, сохраняемые в переменных подобным образом, не используются напрямую. Они называются косвенными файловыми манипулятора-ми (indirect filehandle), поскольку косвенно ссылаются на настоящие манипулято-ры. Два модуля, IO::File (стал стандартным, начиная с версии 5.004) и FileHandle (стандартный с версии 5.000), могут создавать анонимные файловые манипуля-торы. Когда в наших примерах используются модули IO::File или IO::Handle, анало-гичные результаты можно получить с применением модуля FileHandle, посколь-ку сейчас он является интерфейсным модулем (wrapper). Ниже показано, как выглядит программа для поиска "blue" с применением мо-дуля IO::File в чисто объектной записи:
use IO::File:
$input = IO::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 не обязательно использовать объектно-ориентированным способом. В сущности, они представляют собой анонимные файловые манипуляторы и потому могут использоваться везде, где допускаются обычные косвенные манипуляторы. В ре-цепте 7.16 рассматриваются эти модули и префикс *. Модуль IO::File и символи-ческие файловые манипуляторы неоднократно встречаются в этой главе. Стандартные файловые манипуляторы Каждая программа при запуске получает три открытых глобальных файловых манипулятора: STDIN, STDOUT и STDERR. STDIN {стандартный ввод) явля-ется источником входных данных по умолчанию. В STDOUT {стандартный вы-вод) по умолчанию направляются выходные данные. В STDERR {стандартный поток ошибок) по умолчанию направляются предупреждения и ошибки. В интер- активных программах 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)айловый манипу-лятор с файлом или программой, после чего его можно использовать для ввода/ вывода. После завершения работы вызовите для манипулятора функцию close, чтобы разорвать установленную связь. Операционная система работает с файлами через файловые дескрипторы, зна-чение которых определяется функцией fileno. Для большинства файловых опера-ций хватает манипуляторов Perl, однако в рецепте 7.19 показано, как файловый дескриптор преобразуется в файловый манипулятор, используемый в программе. Операции ввода/вывода Основные функции для работы с файлами в Perl - open, print, <...> (чтение записи) и close. Они представляют собой интерфейсные функции для процедур буферизованной библиотеки ввода/вывода С stdio. Функции ввода/вывода Perl документированы в perlfunc( 1) и страницах руководства stdio(3S) вашей систе-мы. В следующей главе операции ввода/вывода - такие, как оператор> о, print, seek и tell - рассматриваются более подробно. Важнейшей .функцией ввода/вывода является функция open. Она получает два аргумента - файловый манипулятор и строку с именем файла и режимом до-ступа. Например, открытие файла /tmp/log для записи и его связывание с мани-пулятором LOG FILE выполняется следующей командой:

open(LOGFILE, "> /tmp/log") or die "Can't write /tmp/log: $!";

Три основных режима доступа - < (чтение), > (запись) и > (добавление). До-полнительные сведения о функции open приведены в рецепте 7.1. При открытии файла или вызове практически любой системной функции* не-обходимо проверять возвращаемое значение. Не каждый вызов open заканчивает-ся успешно; не каждый файл удается прочитать; не каждый фрагмент данных, вы-водимый функцией print, достигает места назначения. Многие программисты для повышения устойчивости своих программ проверяют результаты open, seek, tell и close. Иногда приходится вызывать и другие функции. В документации Perl описаны возвращаемые значения всех функций и операторов. При неудачном за-вершении системная функция возвращает undef (кроме функций wait, waitpid и Системной функцией называется обращение к сервису операционной системы. Термин не имеет отно-шения к функции system в языках С и Perl.
syscall, возвращающих -1). Системное сообщение или код ошибки хранится в переменной $1 и часто используется в die или сообщениях warn. Для чтения записей в Perl применяется оператор <МАНИПУЛЯТОР>, также часто дублируемый функцией readllne. Обычно запись представляет собой одну стро-ку, однако разделитель записей можно изменить (см. главу 8). Если МАНИПУЛЯТОР не указывается, Perl открывает и читает файлы из @ARGV, а если они не указаны - из STDIN. Нестандартные и просто любопытные применения этого факта описаны в рецепте 7.7. С абстрактной точки зрения файл представляет собой обычный поток байтов. Каждый файловый манипулятор ассоциируется с числом, определяющим теку-щую позицию внутри файла. Текущая позиция возвращается функцией tell и устанавливается функцией seek. В рецепте 7.10 мы перезаписываем файл, обхо-дясь без закрытия и повторного открытия, - для этого мы возвращаемся к началу файла функцией seek. Когда надобность в файловом манипуляторе отпадает, закройте его функцией close. Функция получает один аргумент (файловый манипулятор) и возвращает true, если буфер был успешно очищен, а файл - закрыт, и false в противном слу-чае. Закрывать все манипуляторы функцией close необязательно. При открытии файла, который был открыт ранее, Perl сначала неявно закрывает его. Кроме того, все открытые файловые манипуляторы закрываются при завершении программы. Неявное закрытие файлов реализовано для удобства, а не для повышения на-дежности, поскольку вы не узнаете, успешно ли завершилась системная функция. Не все попытки закрытия завершаются успешно. Даже если файл открыт только для чтения, вызов close может завершиться неудачей - например, если доступ к устройству был утрачен из-за сбоя сети. Еще важнее проверять результат close, если файл был открыт для записи, иначе можно просто не заметить переполне-ния диска:
close(FH) or die "FH didn't close: $!"; Усердный программист даже проверяет результат вызова close для STDOUT в кон-це программы на случай, если выходные данные были перенаправлены в команд-ной строке, а выходная файловая система оказалась переполнена. Вообще-то об этом должна заботиться runtime-система, но она этого не делает. Впрочем, проверка 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). Несмотря на отдельные исключения, не следует чередовать их вызовы в программе. Связь между функциями, находящимися в одной строке таблицы, весьма условна. Например, но семантике функция sys read отличается от <. . , >, однако они находятся в одной строке, поскольку выполняют общую задачу - получение входных данных из файлового манипулятора. Таблица 7.1 Функции ввода/вывода в Perl Действие Буферизованные функции Небуферизованные функции Открытие open,sysopen sysopen Закрытие close close Ввод <...>, readline sysread Вывод print syswrite Позиционирование seek, tell__________ sysseek ___ ___ Позиционирование рассматривается в главе 8, однако мы также воспользуем-ся им в рецепте 7.10.

7.1. Открытие файла

Проблема

Известно имя файла. Требуется открыть его для чтения или записи в 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 IO::File; # По аналогии с
open $sink = IO::File->new("> $filename")
or die "Couldn't open $filename for writing: $!\n";
# По аналогии с sysopen
$fh = IO::File->new($filename, 0_WRONLY|0_CREAT)
or die "Couldn't open $filename for reading: $!\n";
# По аналогии с fopen(3) библиотеки
stdio $fh = IO::File->new($filename, "r+")
or die "Couldn't open $filename for read and write: $!\n";

Комментарий

Все операции ввода/вывода осуществляются через файловые манипуляторы независимо от того, упоминаются манипуляторы в программе или нет. Фай-ловые манипуляторы не всегда связаны с конкретными файлами - они также применяются для взаимодействия с другими программами (см. главу 16 <Уп-равление процессами и межпроцессные взаимодействия>) и в сетевых комму-никациях (см. главу 17 <Сокеты>). Функция open также применяется для ра-боты с файловыми дескрипторами, данная возможность рассматривается в рецепте 7.19. Функция open позволяет быстро и удобно связать файловый манипулятор с файлом. Вместе с именем файла передаются сокращенные обозначения стандарт-ных режимов (чтение, запись, чтение/запись, присоединение). Функция не по-зволяет задать права доступа для создаваемых файлов и вообще решить, нужно ли создавать файл. Если вам потребуются подобные возможности, воспользуй-тесь функцией sysopen, которая использует константы модуля Fcnti для управле-ния отдельными компонентами режима (чтение, запись, создание и усечение). Большинство программистов начинает работать с open задолго до первого ис-пользования sysopen. В таблице показано соответствие между режимами функ-ции open (<Файл>), константами sysopen (<Флаги>) и строками fopen(3), переда-ваемыми 10: :File->new (<Символы>). Столбцы <Чтение> и <Запись> показывают, возможно ли чтение или запись для данного файлового манипулятора. <Присое-динение> означает, что выходные данные всегда направляются в конец файла не-зависимо от текущей позиции (в большинстве систем). В режиме усечения функ-ция open уничтожает все существующие данные в открываемом файле.

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_ зависит от операционной си-стемы. Дополнительные сведения можно найти в электронной документации (обычно open (2), но не всегда) или в файле /usr/include/fcntl.h. Обычно встреча-ются следующие константы:
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) процесса. В мас-ке доступа сброшенные биты соответствуют запрещенным правам. Например, если маска равна 027 (группа не может записывать; прочие не могут читать, за-писывать или выполнять), то вызов sysopen с параметром 066 создает файл с пра-вами 0640 (0666&-027 - 0640). Если у вас возникнут затруднения с масками доступа, воспользуйтесь про-стым советом: передавайте значение 0666 для обычных файлов и 0777 для ка-талогов и исполняемых файлов. У пользователя появляется выбор: если ему понадобятся защищенные файлы, то может выбрать маску 022, 027 или антиоб-щественную маску 077. Как правило, решения из области распределения прав должны приниматься не программой, а пользователем. Исключения возникают при записи в файлы, доступ к которым ограничен: почтовые файлы, cookies в Web-броузерах, файлы .rhosts и т. д. Короче говоря, функция sysopen почти никог-да не вызывается с аргументом 0644, так как у пользователя пропадает возмож-ность выбрать более либеральную маску. Приведем примеры практического использования 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.

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, пользователь сможет передать вместо имени (})айла строку "gzip -de bible. gz |" - фильтр автоматически запустит программу распаковки. Вопросы безопасности open актуальны лишь для программ, работающих в осо-бых условиях. Если программа должна работать под управлением чего-то друго-го - например, сценариев CGI или со сменой идентификатора пользователя, - добросовестный программист всегда учтет возможность ввода пользователем собственного имени файла, при котором вызов open для простого чтения превра-тится в перезапись 4)айла или даже запуск другой программы. Параметр команд-ной строки Perl -Т обеспечивает проверку ошибок.

Смотри также: Описание функций open и sysopen в perlfunc(1); рецепты 7.1, 7.7, 16.2, 19.4 и 19.6.

7.3. Тильды в именах файлов

Проблема

Имя файла начинается с тильды (например, -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 для выбора домашнего каталога пользователя из возвращаемого списка. Найденный каталог образует заменяющую строку. Если за тильдой не указано имя пользователя, подставляется либо текущее значение переменной окружения HOME или LOGDIR. Если эти переменные не определены, задается домашний каталог текущего пользователя.

Смотри также: Описание функции getpwnam в perlfunc(1); man-страница getpwnam(2) вашей системы; рецепт 9.6.

7.4. Имена файлов в сообщениях об ошибках

Проблема

Программа работает с файлами, однако в предупреждения и сообщения об ошибках 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.

7.5. Создание временных файлов

Проблема

Требуется создать временный файл и автоматически удалить его при заверше-нии программы. Допустим, вы хотите записать временный конфигурационный файл, который будет передаваться запускаемой программе. Его имя должно быть известно заранее. В других ситуациях нужен временный файл для чтения и запи-си данных, причем его имя вас не интересует.

Решение

Если имя файла не существенно, воспользуйтесь методом класса new_tmpfile модуля IO::File для получения файлового манипулятора, открытого для чтения и записи:
use IO::File;
$fh = IO::File->new_tmpfile
or die "Unable to make new temporary file: $!";
Если имя файла должно быть известно, получите его функцией tmpnam из мо-дуля POSIX и откройте файл самостоятельно:
use IO::File;
use POSIX qw(tmpnam);
# Пытаться получить временное имя файла до тех пор,
# пока не будет найдено несуществующее имя
do { $name = tmpnam() }
until $fh = IO::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 IO::File;
$fh = IO::File->new_tmpfile or die "IO::File->new_tmpfile: $!";
$fh->autorlush(1);
print( $fh "$i\n" while $i++ < 10;
seek($fh, 0, 0);
print "Tmp file has: ", <$rh>;
Во втором варианте создается временный файл, имя которого можно передать другому процессу. Мы вызываем функцию POSIX: :tmpnam, самостоятельно открыва-ем файл и удаляем его после завершения работы. Перед открытием файла мы не проверяем, существует ли файл с таким именем, поскольку при этом может про-изойти подмена - кто-нибудь создаст файл между проверкой и созданием2. Вме-сто этого tmpnam вызывается в цикле, что гарантирует создание нового файла и предотвращает случайное удаление существующих файлов. Теоретически метод new_tmpfile не должен возвращать одинаковые имена разным процессам.

Смотри также: Документация по стандартным модулям IO::File и POSIX; рецепт 7.19; стра-ница руководства tmpnam{3) вашей системы. Но перед вызовом ехес следует присвоить УР хотя бы fileno($fh). 2 См. рецепт 19.4.

7.6. Хранение данных в тексте программы

Проблема

Некоторые данные должны распространяться вместе с программой и интерпрети-роваться как файл, но при этом они не должны находиться в отдельном файле.

Решение

Лексемы __DATA__ и __END__ после исходного текста программы отмечают начало блока данных, который может быть прочитан программой или модулем через фай-ловый манипулятор DATA. В модулях используется лексема __DATA__:
while () { # Обработать строку }
"DATA__ и Данные
Аналогично используется __END__ в главном файле программы:
while () {
# Обработать строку
}
-END__
# Данные

Комментарий

Лексемы __DATA__ и __END__ обозначают логическое завершение модуля или сценария перед физическим концом файла. Текст, находящийся после __ОАТА__ или __END__, может быть прочитан через файловый манипулятор DATA уровня па-кета. Предположим, у нас имеется гипотетический модуль Primes; текст пос-ле __DATA__ в файле Primes.pm может быть прочитан через файловый манипуля-тор Primes::DATA. Лексема __END__ представляет собой синоним __DATA__ в главном пакете. Текст, следующий после лексем __END__ в модулях, недоступен. Появляется возможность отказаться от хранения данных в отдельном файле и перейти к построению автономных программ. Такая возможность нередко ис-пользуется для документирования. Иногда в программах хранятся конфигураци-онные или старые тестовые данные, использованные при разработке программ, - они могут пригодиться в процессе отладки. Манипулятор DATA также применяется для определения размера или даты по-следней модификации текущей программы или модуля. В большинстве систем пе-ременная $0 содержит полное имя файла для работающего сценария. В тех системах, где значение $0 оказывается неверным, можно воспользоваться манипулятором DATA для определения размера, даты модификации и т. д. Вставьте в конец файла специальную лексему __DATA__ (и предупреждение о том, что __DATA__ не следует удалять), и файловый манипулятор DATA будет связан с файлом сценария.
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).


Назад
Вперед