Доступ к файлам
Как мы уже знаем, для доступа
к файлу из программы Perl необходим дескриптор. Дескриптор файла создается функцией
open (), которая является списковой операцией Perl:
open ДЕСКРИПТОР, ИМЯ_ФАЙЛА; open
ДЕСКРИПТОР;
При выполнении операции open с
заданным в параметрах именем файла открывается соответствующий файл и создается
дескриптор этого файла. В качестве дескриптора файла в функции open () можно
использовать выражение — его значение и будет именем дескриптора. Имя файла
задается непосредственно в виде строкового литерала или выражения, значением
которого является строка. Операция open без имени файла открывает файл, имя
которого содержится в скалярной переменной $ДЕСКРИПТОР, которая не может быть
лексической переменной, определенной функцией ту(). Пример 7.1 демонстрирует
использование операции open () для открытия файлов.
#! peri -w
$var = "out.dat";
$FILE4 = "file4.dat";
open FILE1, "in.dat";
# Имя файла задано строкой
open FILE2, $var; # Имя файла'задано
переменной
open FILE3, "/perlourbook/01/".$var;
# Имя файла вычисляется в выражении
open FILE4; # Имя файла в переменной
$FILE4
Замечание
Если задано
не полное имя файла, то открывается файл с указанным именем и расположенный
в том же каталоге, что и программа Perl. Можно задавать полное имя файла (см.
третий оператор open примера 7.1), однако следует иметь в виду, что оно зависит
от используемой операционной системы. Например, в Windows следует обязательно
задавать имя диска: d: /perlourbook/01/Chapterl. doc.
Замечание
В системе
UNIX можно открыть достаточно много файлов, тогда как в DOS и Windows количество
открытых файлов зависит от установленного значения переменной окружения FILE
и варьируется от 20 до 50 одновременно открытых файлов.
Любой файл можно открыть в одном
из следующих режимов: чтения, записи или добавления в конец файла. Это осуществляется
присоединением соответствующего префикса к имени файла: < (чтение), >
(запись), » (добавление). Если префикс опущен, то по умолчанию файл открывается
в режиме чтения. Запись информации в файл, открытый в режиме записи (префикс
>), осуществляется в начало файла, что приводит к уничтожению содержащейся
в нем до его открытия информации. Информация, содержащаяся в файле, открытом
в режиме добавления (префикс »), не уничтожается, новые записи добавляются
в конец файла. Если при открытии файла в режиме записи или добавления не существует
файла с указанным именем, то он создается, что отличает эти режимы открытия
файла от режима чтения, при котором файл должен существовать. В противном случае
операция открытия завершается с ошибкой и соответствующий дескриптор не создается.
Perl позволяет открыть файл еще
в одном режиме — режиме чтения/записи. Для этого перед префиксом чтения <,
записи > или добавления » следует поставить знак плюс +. Отметим различия
между тремя режимами чтения/записи +<, +> и +». Первый и третий
режимы сохраняют содержимое открываемого файла, тогда как открытие файла с использованием
второго режима (+>) сначала очищает содержимое открываемого файла. Третий
режим отличается от первых двух тем, что запись в файл всегда осуществляется
в конец содержимого файла.
Замечание
Некоторые
операционные системы требуют устанавливать указатель чтения/записи файла при
переключении с операций чтения на операции записи. В Perl для этого предназначена
функция seek (), описание которой будет дано несколько позже в этом же параграфе.
Открытие файла и создание для
него дескриптора функцией open () охватывает все практически важные режимы работы
с файлом. Однако возможности этой функции не позволяют задать права доступа
для создаваемых файлов, а также вообще решить, следует ли создавать файл, если
его не существует. Для подобного "тонкого" открытия файлов можно использовать
функцию sysopeno, которая позволяет программисту самому задать отдельные компоненты
режима работы с файлом: чтение, запись, создание, добавление, очистка содержимого
и т. д. Синтаксис этой функции таков:
sysopen ДЕСКРИПТОР, ИМЯ_ФАЙЛА,
ФЛАГ [, РАЗРЕШЕНИЕ];
Здесь параметр ИМЯ_ФАЙЛА представляет
имя файла без префиксов функции open (), определяющих режим открытия файла.
Последний задается третьим параметром ФЛАГ — числом, представляющим результат
операции побитового ИЛИ (|) над константами режимов, определенными
в модуле Fcnti. Состав доступных констант зависит от операционной системы. В
табл. 7.1 перечислены константы режима, встречающиеся практически во всех операционных
системах.
Таблица 7.1. Константы режима доступа к файлу
Константа |
Значение |
0_RDONLY |
Только чтение |
0_WRONLY |
Только запись |
O_RDWR |
Чтение и запись |
O_CREAT |
Создание файла, если он не существует |
О EXCL |
Завершение с ошибкой, если файл уже существует |
0_APPEND |
Добавление в конец файла % |
Права доступа (необязательный параметр РАЗРЕШЕНИЕ) задаются в восьмеричной системе и при их определении учитывается текущее значение маски доступа к процессу, задаваемого функцией umasko. Если этот параметр не задан, то Perl использует значение 0666.
(О правах доступа читайте документацию
Perl для установленной на вашем компьютере операционной системе.)
Совет
Если возникают
затруднения с установкой прав доступа, то придерживайтесь следующего правила:
для обычных файлов передавайте 0666, а для каталогов и исполняемых файлов 0777.
В примере 7.2 собраны операции
открытия файлов функцией open (} и эквивалентные ИМ ОТКРЫТИЯ С ПОМОЩЬЮ фуНКЦИИ
sysopen () .
use Fcnti;
# Только чтение
open FF, "< file.txt";
sysopen FF, "file.txt",
O_RDONLY;
# Только запись (создается, если
не существует,
# и очищается содержимое, если
существует)
open FF, "> file.txt";
sysopen FF, "file.txt",
0_WRONLY | 0_CREAT | OJTRUNC;
# Добавление в конец (создается,
если не существует)
open FF, "» file.txt";
sysopen FF, "file.txt",
OJJRONLY I 0_CREAT I O_APPEND;
# Чтение/запись (файл должен существовать)
open FF, "+< file.txt"; sysopen FF, "file.txt", O_RDWR;
# Чтение/запись (файл очищается)
open FF, "+> file.txt";
sysopen FF, "file.txt",
O_RDWR | 0_CREAT I OJTRUNC;
При открытии файла функции open
о и sysopen о возвращают значение о, если открытие файла с заданным режимом
произошло успешно, и неопределенное значение undef в противном случае. Всегда
следует проверять успешность выполнения операции открытия файла, прекращая выполнение
программы функцией die (). Эта функция отображает список передаваемых ей параметров
и завершает выполнение сценария Perl:
open(FF, "+< $file")
or'die "Нельзя открыть файл $file: $!";
Обратите внимание, в сообщении
функции die () используется специальная переменная $!, в которой хранится системное
сообщение или код ошибки. Эта информация помогает обнаружить и исправить ошибки
в программе. Например, если переменная $fiie содержит имя не существующего файла,
то при выполнении предыдущего оператора пользователь может увидеть сообщение
следующего вида:
Нельзя открыть файл file.txt:
No such file or directory at D:\PERL\EX2.PL line 4.
Английский текст этого сообщения
представляет информацию, содержащуюся в Переменной $!.
Для полноты описания работы с
функцией open о следует сказать, что если имя файла представляет строку "-",
то открываемый файл соответствует стандартному вводу STDIN. Это означает, что
ввод с помощью созданного дескриптора файла осуществляется со стандартного устройства
ввода. Если имя файла задано в виде строки ">-", то это соответствует
выводу на стандартное устройство вывода, представленное в программе дескриптором
STDOUT.
Замечание
Если стандартный
ввод или вывод были перенаправлены, то ввод/вывод с помощью дескрипторов,
соответствующих файлам "-" и ">-", будет осуществляться
в файл, определенный в операции перенаправления стандартного ввода или вывода.
Последнее, что нам хотелось бы
осветить в связи с дескрипторами файлов, — это создание дескриптора-дубликата.
Если в строке имени файла после префикса режима открытия следует амперсанд "&",
то ее оставшаяся часть рассматривается как имя дескриптора файла, а не как имя
открываемого файла. В этом случае создается независимая копия этого дескриптора
с именем, заданным первым параметром функции open <). Оба дескриптора имеют
общий указатель текущей позиции файла, но разные буферы ввода/вывода. Закрытие
одного из дескрипторов не влияет на работу другого. В программах Perl возможность
создания копии дескриптора в основном применяется для восстановления стандартных
файлов ввода/вывода после их перенаправления на другие файлы (пример 7.3).
#! peri -w
# Создание копии дескриптора STDOUT
open(OLDOUT, ">&STDOUT");
# Перенаправление стандартного
вывода
open(STDOUT, "> file.out")
or die "Невозможно перенаправить STDOUT: $!";
# Печать в файл file.out
print "Информация в перенаправленный
STDOUTXn";
# Закрытие перенаправленного дескриптора
стандартного вывода close(STDOUT) or die "Невозможно закрыть STDOUT: $!";
# Восстановить файл стандартного
вывода
open(STDOUT, ">&OLDOUT")
or die "Невозможно восстановить STDOUT: $!";
# Закрыть копию дескриптора стандартного
вывода STDOUT close(OLDOUT) or die "Невозможно закрыть OLDOUT: $!";
# Печать в восстановленный файл
стандартного вывода print "Информация в восстановленный STDOUTXn";
Замечание
В программах
следует избегать работу с одним файлом через несколько дескрипторов-копий.
По завершении работы с файлом
он закрывается функцией close 0. Единственным необязательным параметром этой
функции является дескриптор, ассоциированный с файлом:
close ДЕСКРИПТОР;
Эта функция возвращает значение
Истина, если успешно очищен буфер ввода/вывода и закрыт системный дескриптор
файла. Вызванная без параметра, функция close закрывает файл, связанный с текущим
дескриптором, установленным функцией select 0.
Следует отметить, что закрывать
файлы в программе функцией close о не обязательно. Дело в том, что открытие
нового файла с дескриптором, уже связанным с каким-либо файлом, закрывает этот
старый файл. Более того, при завершении программы все открытые в ней файлы закрываются.
Однако такое неявное закрытие файлов таит в себе потенциальные ошибки из-за
невозможности определить, завершилась ли эта операция корректно. Может оказаться,
что при записи в файл переполнится диск, или будет разорвана связь с удаленным
устройством вывода. Подобные ошибки можно "отловить", если использовать
явное закрытие файла и проверять содержимое специальной переменной $!:
closet FILEIO ) or die "Ошибка
закрытия файла: $!";
Существует еще один нюанс, связанный
с явным закрытием файлов. При чтении из файла специальная переменная $. (если
ее значение не изменено явным образом в программе) хранит номер последней прочитанной
записи файла. При явном закрытии файла функцией close о значение этой переменной
обнуляется, тогда как при неявном закрытии оно остается равным номеру последней
прочитанной записи старого файла и продолжает увеличиваться при операциях чтения
из нового файла.
Чтение информации из файла осуществляется
операцией о, операндом которой является дескриптор файла. В скалярном контексте
при первом выполнении эта операция читает первую запись файла, устанавливая
специальную переменную $., отслеживающую количество прочитанных записей, равной
1. Последующие обращения к операции чтения из файла с тем же дескриптором приводят
к последовательному чтению следующих записей. В списковом контексте эта операция
читает все оставшиеся записи файла и возвращает список, элементами которого
являются записи файла. Разделитель записей хранится в специальной переменной
$/, и по умолчанию им является символ новой строки "\n". Perl позволяет
задать и другой разделитель записей обычной операцией присваивания переменной
$/ нового символа разделителя записей. В примере 7.4 демонстрируются некоторые
приемы чтения из файла.
#! peri -w
open(Fl, "in.dat") or
die "Ошибка открытия файла: $!";
open(F2, "out.dat") or
die "Ошибка открытия файла: $!";
$linel = <F1>; # Первая запись
файла in.dat $line2 = <F1>; # Вторая запись файла in.dat
@rest =• <F1>; # Оставшиеся
записи файла in.dat
$/=":"; I Задание другого
разделителя записей файла @f2 = <F2>;
# Печать прочитанных записей файла
out.dat for($i=0; $i<=$#f2; $i++) { print "$f2[$i]\n";
}
$/ = "\n"; # Восстановление
умалчиваемого разделителя записей
close(Fl) or die $!; close(F2)
or die $!;
open(F3, "out.dat") or
die "Ошибка открытия файла: $!"; print <F3>; # Печать
всего файла close(F3) or die $!;
Несколько комментариев к программе
примера 7.4. В переменные $iinel и $iine2 читаются соответственно первая и вторая
строка файла in.dat, так как используется умалчиваемый разделитель записей "\п".
Элементы массива @rest хранят строки с третьей по последнюю этого же файла:
в операторе присваивания операция чтения <FI> выполняется в списковом
контексте.
Перед чтением записей файла out.dat
устанавливается новый разделитель записей — символ ":". Если файл
out.dat, например, содержит только одну строку
111: 222: 333: Конец
то элементы массива @ f г
будут содержать следующие значения:
$f2[0] = "111:" $f2[l]
= "222:" $f2[2] = "333:" $f2[3] = "Конец"
Замечание
Если при создании
файла out.dat его единственная строка завершена переходом на новую строку (нажата
клавиша <Enter>), то $f2[3], будет содержать строку "конец\п".
При достижении конца файла операция
о возвращает неопределенное значение, которое трактуется как Ложь. Это обстоятельство
обычно используется для организации чтения записей файла в цикле:
while($line = <F1>) {
print $line; f Печать очередной
строки связанного
# с дескриптором F1 файла } •
Запись в файл, открытый в режиме
записи или добавления, осуществляется функцией print () с первым параметром,
являющимся дескриптором файла:
print ДЕСКРИПТОР СПИСОК_ВЫВОДД;
Эта операция записывает содержимое
элементов списка в том порядке, в котором они определены в вызове функции, и
не добавляет в конец списка разделителя записей. Об этом должен позаботиться
сам программист:
$/=":"; # Разделитель
записей
print Fl @recll, $/; # Запись в
файл первой записи
print Fl @rec!2, $/; tt Запись
в файл второй записи
Замечание
No comma allowed after
filehandle
Если в функции print не указан
дескриптор файла, то по умолчанию вывод осуществляется в стандартный файл вывода
с дескриптором STDOUT. Эту установку можно изменить функцией select (). Вызванная
без параметров, она возвращает текущий умалчиваемый дескриптор для вывода функциями
print () и write (). Если ей передается единственный параметр, то этот параметр
должен быть дескриптором файла. В этом случае она также возвращает текущий умалчиваемый
дескриптор и меняет его на дескриптор, определенный переданным ей параметром.
$oldfilehandle = select(Fl); I
Сохранение текущего дескриптора по
# умолчанию и назначение нового
F1
print $line; # Вывод в дескриптор
F1 select($oldfilehandle); # Восстановление старого дескриптора
# по умолчанию print $line; # Вывод
в старый дескриптор
Файлы в Perl интерпретируются
как неструктурированные потоки байтов. При работе с файлом через дескриптор
отслеживается его текущая позиция. Операции чтения/записи выполняются
с текущей позиции файла. Если, например, была прочитана запись длиной 80 байт,
то следующая операция чтения или записи начнется с 81 байта файла. Для определения
текущей позиции в файле используется функция tell (), единственным параметром
которой может быть дескриптор файла. Она возвращает текущую позицию в связанном
с дескриптором файле. Эта же функция без параметра возвращает текущую позицию
в файле, для которого была в программе выполнена последняя операция чтения.
Текущая позиция в файле автоматически
изменяется в соответствии с выполненными операциями чтения/записи. Ее можно
изменить с помощью функции seek о, которой передаются в качестве параметров
дескриптор файла, смещение и точка отсчета. Для связанного с дескриптором файла
устанавливается новая текущая позиция, смещенная на заданное параметром СМЕЩЕНИЕ
число байт относительно точки отсчета:
seek ДЕСКРИПТОР, СМЕЩЕНИЕ, TO4KAJDTC4ETA;
Параметр ТОЧКА_ОТСЧЕТА может принимать
одно из трех значений: о — начало файла, 1 — текущая позиция, 2 — конец файла.
Смещение может быть как положительным, так и отрицательным. Обычно оно отрицательно
для смещения относительно конца файла и положительно для смещения относительно
начала файла. Для задания точки отсчета можно воспользоваться константами SEEK_SET,
SEEK_CUR и SEEK_END из модуля ю: :Seekabie, которые соответствуют началу файла,
текущей позиции и концу файла. Естественно, необходимо подключить этот модуль
к программе с помощью ключевого слова use. Например, следующие операторы устанавливают
одинаковые текущие позиции в файлах:
use 10::Seekable; seek FILE1, 5,
0; seek FILE2, 5, SEEK_SET;
Для перехода в начало или в конец
файла следует использовать нулевое смещение относительно соответствующих точек
отсчета при обращении к функции seek ():
seek FILE1, 0, 0; # Переход в начало
файла seek FILE1, 0, 2; § Переход в конец файла
Кроме операции чтения записей
файла о, Perl предоставляет еще два способа чтения информации из файла: функции
getc () и read (). Первая читает один байт из файла, тогда как вторая читает
записи фиксированной длины.
Функция getc возвращает символ
в текущей позиции файла, дескриптор которого передан ей в качестве параметра,
или неопределенное значение в случае достижения конца файла или возникновении
ошибки. Если функция вызывается без параметра, то она читает символ из стандартного
файла ввода STDIN.
getc; t Чтение символа из STDIN
getc Fl; # Чтение символа в текущей
позиции файла с дескриптором F1
Функции read () передаются три
или четыре параметра и ее синтаксис имеет вид:
read ДЕСКРИПТОР, ПЕРЕМЕННАЯ, ДЛИНА
[,СМЕЩЕНИЕ] ;
Она читает количество байтов,
определенное значением параметра ДЛИНА, в скалярную переменную, определяемую
параметром ПЕРЕМЕННАЯ, из файла с дескриптором, заданным первым параметром ДЕСКРИПТОР.
Возвращаемое значение — действительное количество прочитанных байтов, о при
попытке чтения в позиции конца файла и неопределенное значение в случае возникновения
ошибки. Параметр СМЕЩЕНИЕ определяет количество сохраняемых байтов из содержимого
переменной ПЕРЕМЕННАЯ, т. е. запись прочитанных из файла данных будет добавлена
к содержимому переменной после байта, определяемого значением параметра СМЕЩЕНИЕ.
Отрицательное значение смещения -п (п — целое число) означает, что из содержимого
переменной ПЕРЕМЕННАЯ отбрасываются последние п байтов и к оставшейся строке
добавляется запись, прочитанная из файла. Пример 7.5 демонстрирует чтение записей
фиксированной длины в предположении, что файл in.dat содержит три строки данных:
One Two Three
#! peri -w
open(Fl, "in.dat") or
die "Ошибка открытия файла: $!";
$string = "1234567890";
read Fl, $string, 6; I Чтение шести
байт в переменную без смещения
print $string,"\n"; #
$string = "OneXnTw"
read Fl, $string, 6, length($string);
print $string,"\n"; #
$string = "One\nTwo\nThre"
Функция length о возвращает количество
символов (байтов) в строковых данных, хранящихся в скалярной переменной, переданной
ей в качестве параметра. После выполнения первой операции чтения содержимое
переменной $string было уничтожено, так как эта функция read о вызывалась без
смещения. Тогда как при втором чтении хранившиеся данные в переменной $string
были полностью сохранены.
Операции о, print, read, seek
и tell относятся к операциям буферизованного ввода/вывода, т. е. они для повышения
скорости выполнения используют буферы. Perl для выполнения операций чтения из
файла и записи в файл предлагает также аналоги перечисленных функций, не использующие
буферы при выполнении соответствующих операций с содержимым файла.
Функции sysread и syswrite являются
не буферизованной заменой операции
о И ФУНКЦИИ
print, а ФУНКЦИЯ sysseek Заменяет ФУНКЦИИ seek И tell.
Функции не буферизованного чтения
и записи получают одинаковые параметры, которые соответствуют параметрам функции
read:
sysread ДЕСКРИПТОР, ПЕРЕМЕННАЯ,
ДЛИНА [,СМЕЩЕНИЕ]; syswrite ДЕСКРИПТОР, ПЕРЕМЕННАЯ, ДЛИНА [.СМЕЩЕНИЕ];
Смысл всех параметров аналогичен
параметрам функции read (). Возвращаемым значением этих функций является истинное
количество прочитанных/записанных байт, о в случае достижения конца файла или
undef при возникновении ошибки.
Параметры функции sysseek о полностью
соответствуют параметрам функции seek():
sysseek ДЕСКРИПТОР, СМЕЩЕНИЕ, ТОЧКА_ОТСЧЕТА;
Все, сказанное относительно использования
функции seek о, полностью переносится и на ее не буферизованный аналог.
Функциональность буферизованной
операции tell () реализуется следующим вызовом функции sysseek:
$position = sysseek Fl, 0, 1;
# Текущая позиция указателя файла
Пример 7.6 демонстрирует использование
не буферизованных функций, ввода/вывода для обработки содержимого файла.
#! peri -w use Fcntl;
# Открытие файла в режиме чтение/запись
sysopen Fl, "in.dat", OJRDWR;
# Чтение блока в 14 байт
$read = sysread Fl, $string, 14;
warn "Прочитано $read байт
вместо 14\n" if $read != 14;
# Установка текущей позиции (на
15 байт). $position = sysseek Fl, 0, 1; die "Ошибка позиционирования: $!\п"
unless defined $position;
# Запись строки в текущей позиции
$string = "Новое значение"; $written = syswrite Fl, $string, length($string);
die "Ошибка записи: $!\n"
if $written != length($string); # Закрытие файла
close Fl or die $!;
При работе с не буферизованными
функциями ввода/вывода следует всегда проверять завершение операции чтения,
записи или позиционирования. Стандартная система ввода/вывода, через которую
реализуется буферизованный ввод/вывод, сама проверяет и отвечает за завершение
указанных операций, если процесс был прерван на середине записи. При не буферизованном
вводе/выводе об этом должен позаботиться программист.
Совет
При работе с одним
и тем же файлом не следует смешивать вызовы буферизованных и не буферизованных
функций ввода/вывода. Подобная практика может приводить к непредсказуемым коллизиям.