Листинг 6.7. Открытие файлов
use Fcntl;
# Только чтение
open FF, "< file.txt";
sysopen FF, "file.txt", O_RDONLY;
# Только запись (создается, если не существует,
# и очищается содержимое, если существует)
open FF, "> file.txt";
sysopen FF, "file.txt", O_WRONLY | O_CREAT | O_TRUNC;
# Добавление в конец (создается, если не существует)
open FF, ">> file.txt";
sysopen FF, "file.txt", O_WRONLY | O_CREAT | O_APPEND;
# Чтение/запись (файл должен существовать)
open FF, "+< file.txt";
sysopen FF, "file.txt", O_RDWR;
# Чтение/запись (файл очищается)
open FF, "+> file.txt";
sysopen FF, "file.txt", O_RDWR | O_CREAT | O_TRUNC;
При открытии файла функции open( ) и sysopen( ) возвращают значение 0, если открытие файла с заданным
режимом произошло успению, и неопределенное значение
undef
в противном случае. Еще раз напомним, что всегда следует проверять успешность выполнения операции
открытия файла, прекращая выполнение программы функцией
die( ).
В отображаемом сообщении функции die( ) можно использовать специальную переменную $!, в которой
хранится системное сообщение или код ошибки. Эта информация помогает обнаружить и исправить ошибки в
программе. Например, если в операторе
open FH, "$file" or die "Нельзя открыть файл $file: $!";
переменная $file содержит имя несуществующего файла, то при его выполнении пользователь может увидеть
сообщение следующего вида:
Нельзя открыть файл file.txt: No such file or directory at ex06-00.pl line 4.
Для полноты описания работы с функцией open( ) следует сказать, что если имя файла представляет
строку "-", то открываемый файл соответствует стандартному устройству ввода с дескриптором STDIN.
Значение, равное строке ">-", соответствует выводу на стандартное устройство вывода с дескриптором STDOUT.
ПРИМЕЧАНИЕ Если стандартный ввод или вывод были переназначены при запуске сценария из командной строки,
то операции ввода-вывода с помощью дескрипторов, соответствующих файлам "-" и ">-", будут
осуществляться в файлы, определенные во время переназначения стандартного ввода или вывода.
Последнее, что нам хотелось бы рассмотреть в связи с дескрипторами файлов, — это создание
дескриптора-дубликата. Эта возможность реализуется только при использовании функции open( ) с одним
или двумя параметрами. Если в параметре имени файла используется строка, начинающаяся с любого префикса
режима открытия файла, за которым следует амперсанд (&), то ее оставшаяся часть рассматривается не как
имя открываемого файла, а как дескриптор уже открытого файла или числовой дескриптор файла, если она
представляет числовое значение. В этом случае создается еще один дескриптор с именем, определяемым
первым параметром функции open( ), ассоциированный с уже открытым файлом, то есть создается
дескриптор-дубликат. Оба дескриптора имеют общий указатель текущей позиции файла, но разные буферы
ввода-вывода. Закрытие одного из дескрипторов не влияет на работу другого.
Ранее мы упоминали, что невозможно восстановить ввод-вывод на стандартные устройства ввода-вывода
после их переназначения в программе путем открытия файлов с дескрипторами STDIN, STDOUT или STDERR.
Так вот, использование дескрипторов-дубликатов позволяет осуществить восстановление стандартных
устройств ввода-вывода после их программного переназначения (листинг 6.8).
Листинг 6.8. Переназначение и восстановление стандартного вывода
#! perl -w
# Создание копии текущего дескриптора STDOUT
open(OLDSTDOUT, ">&STDOUT");
# Переназначение стандартного вывода
open(STDOUT, "> file.out") or die "Невозможно переназначить STDOUT: $!";
# Печать в файл file.out
print "Информация в переназначенный STDOUT\n":
# Закрытие переназначенного стандартного вывода
close(STDOUT) or die "Невозможно закрыть STDOUT: $!";
# Восстановить дескриптор стандартного устройства вывода
open(STDOUT, ">&OLDSTDOUT") or die "Невозможно восстановить STDOUT: $!";
# Закрыть копию дескриптора стандартного устройства вывода
close(OLDSTDOUT) or die "Невозможно закрыть OLDOUT: $!";
# Печать в восстановленный файл стандартного вывода
print "Информация в восстановленный STDOUT\n";
В начале программы из листинга 6.8 создается дескриптор-дубликат на стандартное устройство вывода,
которое далее переназначается на вывод в некоторый файл. Когда необходимо снова ассоциировать дескриптор
STDOUT со стандартным устройством вывода, то для этих целей используется созданный в начале программы
дескриптор-дубликат.
Казалось бы, зачем так мудрить, ведь после переназначения стандартного устройства вывода можно
дескриптор STDOUT снова ассоциировать со стандартным устройством вывода, открыв с этим дескриптором
специальный файл ">-"? Однако такая схема не сработает. После «восстановления» стандартного устройства
вывода любая печать в STDOUT будет осуществлять вывод в тот файл, на который был первоначально
переназначен STDOUT. В действительности операция open STDOUT, ">-" снова ассоциирует дескриптор STOUT с
самим собой, то есть в конечном счете с тем файлом, на который он был ранее переназначен!
Попытка перед операцией «восстановления» STDOUT закрыть его приведет вообще к отключению печати на
стандартное устройство вывода.
По завершении работы с файлом он закрывается функцией close( ). Единственным необязательным
параметром этой функции является дескриптор, ассоциированный с файлом:
close ДЕСКРИПТОР;
Эта функция возвращает значение «истина», если успешно очищен буфер ввода-вывода и закрыт системный
числовой дескриптор файла. Вызванная без параметра, функция close закрывает файл, связанный с текущим
дескриптором, установленным функцией
select( ).
Следует отметить, что закрывать файлы в программе функцией close( ) не обязательно. Дело в том, что
открытие нового файла с дескриптором, уже связанным с каким-либо файлом, закрывает этот старый файл.
Более того, при завершении программы все открытые в ней файлы закрываются. Однако такое неявное
закрытие файлов таит в себе потенциальные ошибки из-за невозможности определить, завершилась ли эта
операция корректно. Может оказаться, что при записи в файл переполнится диск или будет разорвана связь
с удаленным устройством вывода. Подобные ошибки можно «отловить», если использовать явное закрытие файла
и проверять содержимое специальной переменной $!:
close (FILEIO ) or die "Ошибка закрытия файла: $!";
Существует еще один нюанс, связанный с явным закрытием файлов. При чтении из файла специальная
переменная $. (если ее значение не изменено явным образом в программе) хранит номер последней прочитанной
записи файла. При явном закрытии файла функцией close( ) значение этой переменной обнуляется, тогда как
при неявном закрытии оно остается равным номеру последней прочитанной записи старого файла и продолжает
увеличиваться при операциях чтения из нового файла.