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