Как мы уже знаем, для доступа к файлу из программы 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 Запись в файл второй записи
Замечание
Между дескриптором и первым элементом списка вывода не должно
быть запятой. Если такое случится, то компилятор peri выдаст
ошибку:
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 $!;
При работе с не
буферизованными функциями ввода/вывода следует всегда проверять
завершение операции чтения, записи или позиционирования. Стандартная
система ввода/вывода, через которую реализуется буферизованный
ввод/вывод, сама проверяет и отвечает за завершение указанных
операций, если процесс был прерван на середине записи. При
не буферизованном вводе/выводе об этом должен позаботиться
программист.
Совет
При работе с одним и тем же файлом не следует смешивать вызовы
буферизованных и не буферизованных функций ввода/вывода. Подобная
практика может приводить к непредсказуемым коллизиям.
|