В Perl предусмотрен набор унарных операций, возвращающих значение только одного поля структуры индексного дескриптора. Эти операции в документации называются "операциями -X", так как их названия состоят из дефиса с последующим единственным символом. Все они являются унарными именованными операциями и имеют свой приоритет в сложных выражениях.
Полный перечень унарных операций проверки файлов
R Файл может читаться эффективным uid/gid -w Записывать в файл может эффективный uid/gid -x Файл может выполняться эффективным uid/gid -o Владельцем файла является эффективный uid/gid -R Файл может читаться действительным uid/gid -W Записывать в файл может действительный uid/gid -X Файл может выполняться действительным uid/gid -O Владельцем файла является действительный uid/gid -e Файл существует -z Размер файла равен нулю -s Размер файла отличен от нуля (возвращает размер) -f Файл является обычным (plain) файлом -d Файл является каталогом -l Файл является символической ссылкой -p Файл является именованным програмным каналом (FIFO) или проверяемый дескриптор связан с програмным каналом -S Файл является сокетом -b Файл является специальным блочным файлом -c Файл является специальным символьным файлом -t Дескриптор файла связан с терминалом -u У файла установлен бит setuid -g У файла установлен бит setgid -k У файла установлен бит запрета (sticky bit) -T Файл является текстовым файлом -B Файл является двоичным (противоположным текстовому) -M Возраст файла в днях на момент выполнения программы -A То же для врмени последнего обращения к файлу -C То же для время последней модификации индексного дескриптора файла
Унарные операции применяются к строке, содержащей имя файла, к выражению, вычисляемым значением которого является имя файла, или к файловому дескриптору Perl. Если параметр операции не задан, то она тестирует файл, чье имя содержится в специальной переменной $_. Каждая операция проверки атрибута файла возвращает 1, если файл обладает соответствующим атрибутом, пустую строку "" в противном случае и неопределенное значение undef, если указанный в параметре файл не существует.
Несколько слов об алгоритме определения текстовых и двоичных файлов (операции -T и -B). Эти операции анализируют содержимое первого блока файла на наличие "странных" символов - необычных управляющих последовательностей или байтов с установленными старшими битами. Если обнаружено достаточно большое количество подобных символов (больше 30%), то файл считается двоичным, иначе текстовым. Любой файл с пустым первым блоком рассматривается как двоичный.
Если эти операции применяются к файловым дескрипторам Perl, то проверяется содержимое буфера ввода/вывода, а не первого блока файла. Обе эти операции, примененные к файловым дескрипторам, возвращают булево значение Истина, если связанный с дескриптором файл пуст или установлен на конец файла.
При выполнении унарных именованных операций проверки файла на самом деле неявно вызывается функция stat(), причем результаты ее вычисления кэшируются, что позволяет использовать специальный файловый дескриптор _ для ускорения множественных проверок файла:
If(-s("filename") && -T _) { # Что-то делаем для текстовых файлов не нулевого размера. . . . . . . . . . . . . . }
chdir
Изменение текущего рабочего каталога на каталог, определяемый значением параметра ВЫРАЖЕНИЕ. Если параметр опущен, домашний каталог становится текущим. Возвращает бклево значение Истина в случае успешного выполнения операции замены текущего каталога и Ложь в противном случае.
Chdir [ВЫРАЖЕНИЕ]
chmod
chmod СПИСОКФункция chmod() изменяет права доступа для файлов, представленных в списке, переданном ей в качестве параметра. Первым элементом этого списка должно быть трехзначное восьмеричное число, задающее права доступа для владельца, пользователей из группы, в которую входит владелец, и прочих пользователей. Каждая восьмеричная цифра определяет право на чтение файла, запись в файл и его выполнение (в случае если файл представляет выполняемую программу) для указанных выше групп пользователей. Установленные биты ее двоичного представления отражают соответствующие права доступа к файлу. Например, если установлены все три бита (восьмеричное число 7), то соответствующая группа пользователей обладает всеми перечисленными правами: может читать из файла, записывать в файл и выполнять его. Значение равное 6 определяет право на чтение и запись, 5 позволяет читать из файла, выполнять его, но не позволяет записывать в этот файл и т.д. Обычно не выполняемый файл создается с режимом доступа 0666 - все пользователи могут читать и записывать информацию в файл, выполняемый файл - с режимом 0777. Если владелец файла желает ограничить запись в файл пользователей не его группы, то следует выполнить следующий оператор:
Chmod 0664, "file.dat";
Возвращаемым значением функции chmod(), как и функции chown(), является количество файлов из списка, для которых операция изменения прав доступа завершилась успешно.
В операционных системах DOS и Windows имеет значение только установка режимов доступа владельца.
chown
chown СПИСОКЛюбой пользователь, создавший собственный файл, считается его владельцем. Изменить владельца файла из сценария Perl можно функцией chown(). Параметром этой функции является список, первые два элемента которого должны представлять числовые идентификаторы uid и gid. Остальные элементы списка являются именами файлов, для которых изменяется владелец. Эта функция возвращает количество файлов, для которых операция изменения владельца и группы прошла успешно.
Пример:
@list = (234, 3, "file1.dat", "file2.dat"); $number = chown(@list); warn "Изменился владелец не у всех файлов!" if $number != @list-2;
Изменить владельца файла может только сам владелец или суперпользователь (обычно системный администратор) системы UNIX. В операционных системах с файловой системой отличной от UNIX (DOS, Windows) эта функция отрабатывает, но ее установки не влияют на доступ к файлу.
chroot
Определяет новый корневой каталог для всех относительных (начинающихся с косой черты "/") имен файлов процесса пользователя и порожденных им процессов. Не меняет текущий рабочий каталог. В отсутствии параметра используется значение специальной переменной $_. Может вызываться только суперпользователем.
Chroot ИМЯ_КАТАЛОГА
close
close ДЕСКРИПТОРПо завершению работы с файлом он закрывается функцией close(). Единственным необязательным параметром этой функции является дескриптор, ассоциированный с файлом.
Эта функция возвращает значение Истина, если успешно очищен буфер ввода/вывода и закрыт системный дескриптор файла. Вызванная без параметра, функция close закрывает файл, связанный с текущим дескриптором, установленным функцией select().
При возникновении ошибок закрытия файла их можно обнаружить применяя специальную переменную $!: close (FILE) or die "Ошибка закрытия файла: $!";
closedir
Закрывает каталог, ассоциированный с дескриптором каталога, заданным параметром ДЕСКРИПТОР. Возвращает булево значение Истина, если каталог успешно закрыт.
Closedir ДЕСКРИПТОР
fcntl
Реализует системную команду Unix fcntl(2). Перед использованием следует получить доступ к определениям системных констант оператором use Fcntl.
Возвращаемое значение: если системная функция возвращает -1, то функция Perl - неопределенное значение; если системная функция возвращает 0, то функция Perl строку "0 but true"; если системная функция возвращает какое-либо другое значение, функция Perl возвращает это же значение.
Fcntl ДЕСКРИПТОР, ФУНКЦИЯ, СКАЛЯР
glob
Возвращает найденные в текущем каталоге файлы, имена которых удовлетворяют заданному шаблону (с использованием метасимволов Unix "*","?"). Значением выражения должна быть строка, содержащая шаблон имен файлов.
Glob ВЫРАЖЕНИЕ
ioctl
Реализует системную команду Unix ioctl(2). Перед использованием следует получить доступ к определениям системных констант оператором require "ioctl.ph";
Возвращаемое значение:
- если системная функция возвращает -1, то функция Perl - неопределенное значение;
- если системная функция возвращает 0, то функция Perl строку "0 but true";
- если системная функция возвращает какое-либо другое значение, функция Perl возвращает это же значение.
link
Link СТАРЫЙ, НОВЫЙ
lstat
Возвращает список значений полей структуры индекснего дескриптора символической ссылки на файл. Если параметр опущен, то используется значение специальной переменной $_.
Lstat [ДЕСКРИПТОР] lstat [ВЫРАЖЕНИЕ]
Используется для получения информации о символических ссылках. Возвращает список значений полей структуры индексного дескриптора самой ссылки, а не файла, на который она ссылается. Эта функция работает аналогично функции stat().
mkdir
Создание нового каталога с именем, заданным в параметре КАТАЛОГ, и режимом доступа, определяемым параметром РЕЖИМ. При успешном создании каталога возвращает булево значение Истина, в противном случае Ложь и в переменную $! заносится сообщение об ошибке.
Mkdir КАТАЛОГ, РЕЖИМ
open
open ДЕСКРИПТОР, ИМЯ_ФАЙЛА; open ДЕСКРИПТОР;Для доступа к файлу из программы Perl необходим дескриптор. Для создания дескриптора используется функция open(). При выполнении операции open с заданым в параметрах именем файла открывается соответствующий файл и создается дескриптор этого файла. В качестве дескриптора файла можно использовать выражение - его значение и будет именем дескриптора. Имя файла задается непосредственно в виде строкового литерала или выражения, значением которого является строка. Операция open без имени файла открывает файл, имя которого содержится в скалярной переменной $ДЕСКРИПТОР, которая не может быть лексической переменной, определенной функцией my().
Пример:
#! perl -w $var = "out.dat"; $FILE4 = "file4.dat"; open FILE1, "in.dat"; # Имя файла задано строкой open FILE2, $var; # Имя файла задано переменной open FILE3, "/perlourbook/01/".$var; # Имя файла вычисляется в выражении open FILE4; # Имя файла в переменной $FILE4
Если задано не полное имя файла, то открывается файл с указанным именем и расположенный в том же каталоге, что и программа Perl. Можно задавать полное имя файла, однако следует иметь в виду, что оно зависит от используемой операйионной системы. Например, в Windows следует обязательно задавать имя диска: d:/perlbook/file1.doc
Любой файл можно открыть в одном из следующих режимов: чтения, записи или добавления в конец файла. Это осуществляется присоединением соответствующего префикса к имени файла:
- < (чтение)
- > (запись)
- >> (добавление)
Если префикс опущен, то по умолчанию файл открывается в режиме чтения.
Запись информации в файл, открытый в режиме записи, осуществляется в начало файла, что приводит к уничтожению содержащейся в нем до его открытия информации.
Информация, содержащаяся в файле, открытом в режиме добавления, не уничтожается, новые записи добавляются в конец файла.
Если при открытии файла в режиме записи или добавления не существует файла с указанным именем, то он создается, что оличает эти режимы открытия файла от режима чтения, при котором файл должен существовать. В противном случае операция открытия завершается с ошибкой и соответствующий дескриптор не создается.
Perl позволяет открыть файл еще в одном режиме - режиме чтения/записи.
Для этого перед префиксом чтения <, записи > или добавления >> следует поставить знак +.
- +< - сохраняют содержимое открываемого файла
- +> - сначало очищает содержимое открываемого файла
- +>> - сохраняют содержимое открываемого файла, запись в файл всегда осуществляется в конец содержимого файла
opendir
Открытие каталога, имя которого равно значению параметра ВЫРАЖЕНИЕ, и связывает его с дескриптором, определяемым параметром ДЕСКРИПТОР. Имена дескрипторов каталогов хранаятся в собственном пространстве имен таблицы имен Perl.
Opendir ДЕСКРИПТОР, ВЫРАЖЕНИЕ
readlink
Возвращает значение сиволической ссылки, определяемой параметром ВЫРАЖЕНИЕ, если символические ссылки реализуются операционной системой; в противном случае - фатальная ошибка. Если при получении значения символической ссылки были получены системные ошибки, возвращает неопределенное значение и в специальную переменную $! заносится сообщение об ошибке. Если параметр опущен, используется значение переменной $_.
Readlink [ВЫРАЖЕНИЕ]
rename
Переименовывает файл. Возвращает 1 в случае успешного переименования и 0 в противном случае.
Rename СТАРОЕ_ИМЯ, НОВОЕ_ИМЯ
stat
В файловой структуре UNIX информация о файле храниться в его индексном дескрипторе (inode). Структура индексного дескриптора состоит из 13 полей, для которых используются специальные обозначения:
Поле | Описание |
dev | Номер устройства в файловой системе |
ino | Номер индексного дескриптора |
mode | Режим файла (тип и права доступа) |
nlink | Количество жестких ссылок на файл (в отсутствии ссылок равно 1) |
uid | Числовой идентификатор владельца файла |
gid | Числовой идентификатор группы владельца файла |
rdev | Идентификатор устройства (только для специальных файлов) |
size | Размер файла в байтах |
atime | Время последнего обращения к файлу с начала эпохи |
mtime | Время последнего изменения файла с начала эпохи |
ctime | Время изменения индексного дескриптора с начала эпохи |
blksize | Предпочтительный размер блока для операций ввода/вывода |
blocks | Фактическое количество выделенных блоков для размещения файла |
Не все перечисленные поля структуры индексного дескриптора поддерживаются всеми файловыми системами.
Функция stat() предназначена для получения значений полей структуры индексного дескриптора файла. Ее единственным параметорм может быть либо имя файла, либо дескриптор открытого в программе файла. Она возвращает список из 13 элементов, содержащих значения полей структуры индексного дескриптора файла в том порядке, как они перечислены в таблице.
Типичное использование в программе Perl представлено ниже:
($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size, $atime,$mtime,$ctime,$blksize,$blocks) = stat($filename);
Присваивание значение полей списку скалярных переменных с идентификаторами, соответствующими названиям полей, способствует лучшей читаемости программы, чем присваивание массиву скаляров:
@inode = stat($filename);
В последнем случае получить значение соответствующего поля можно только с помощью индекса, что не совсем удобно, так как нужно помнить номер нужного поля структуры.
Если при обращении к функции stat() не указан параметр, то она возвращает структуру индексного дескриптора файла, чье имя содержится в специальной переменной $_.
Функция получения информации о файле при успешном выполнении в списковом контексте возвращает список значений полей структуры индексного дескриптора файла или пустой список в случае неудачного завершения. В скалярном контексте она возвращает булево значение Истина или Ложь в зависимости от результатов своего выполнения.
Для удобства использования информации о файле функция stat() при успешном выполнении кэширует полученные значения полей. Если вызвать эту функцию со специальным дескриптором файла _ (символ подчеркивания), то она возвратит информацию, хранящуюся в кэше от предыдущего ее вызова. Это позволяет проверять различные атрибуты файла без повторного вызова функции stat() или сохранения результатов ее выполнения в переменных программы.
Функцию stat() можно использовать для получения структуры индексного дескриптора не только файла, но и жестких ссылок на него, а также каталогов, так как они являются также файлами, блоки данных которых содержат имена файлов каталога и их числовых индексных дескрипторов.
symlink
Symlink СТАРОЕ_ИМЯ, НОВОЕ_ИМЯ
umask
Устанавливает маску режима доступа процесса, заданную значением параметра ВЫРАЖЕНИЕ (восьмеричное число), и возвращает предыдущее значение маски режима доступа.
Umask ВЫРАЖЕНИЕ
unlink
Удаление файлов, определенных параметром СПИСОК. Возвращает количество успешно удаленных файлов.
Unlink СПИСОК
utime
utime СПИСОКВ структуре индексного дескриптора файла существует три поля, в которых храниться время последнего обращения (atime) к файлу, его изменения (mtime) файла и изменения индексного дескриптора (ctime). Функцией utime() можно изменить время последнего обращения и модификации файла. Ее параметром является список, содержащий имена обрабатываемых файлов, причем первые два элемента списка - числовые значения нового времени последнего доступа и модификации:
@files = ("file1.dat", "file2.dat"); $now = time; utime $now, $now, @files;
В этом фрагменте кода время последнего доступа и модификации файлов из списка @files изменяется на текущее время, полученное с помощью функции time.
Отметим, что при выполнении функции utime() изменяется и время последней модификации индексного дескриптора (ctime) - оно устанавливается равным текущему времени. Возвращаемым значением является количество файлов, для которых операция изменения времени последнего доступа и модификации прошла успешно.
Каждый раз, когда необходимо получить доступ к файлу на диске, необходимо создать новый дескриптор и открыть его. Для открытия дескрипторов используется функция open:
open (дескриптор_файла, путь)
Путь указывает, какой файл необходимо открыть, поэтому, если не указан полный путь, например с:/windows/system/, функция open попытается открыть файл в текущем каталоге. При успешном выполнении функция open возвращает ненулевое значение (Истина), при неудачном - возвращается undef (Ложь), например:
if (open(MYFILE, "mydatafile")) {
#Выполняется при успешной открытии
} else {
print "Ошибка при открытии файла mydatafile\n";
exit 1;
}
Во многих программах Perl подобный синтаксис "открыть или сообщить об ошибке" может быть реализован с помощью функции die
. Функция die останавливает выполнение программы и выводит сообщение об ошибке:
Died at имя_сценария line xxx
Здесь имя сценария - название программы на Perl, xxx - номер строки, в которой встретилась функция die. Функции die и open часто используются вместе следующим образом:
open(MYTEXT, "novel.txt") || die;
Программа или открывает файл, или прекращает свое выполнение. Если open завершилась неудачно, возвратив ложное значение, вступает в действие логический оператор ИЛИ (| |) . В результате будет вычисляться аргумент, находящийся в правой части оператора (в данном случае - функция die). При удачном выполнении функции open правая часть логического выражения не вычисляется. Иногда используют другой вид логического ИЛИ - or.
Для закрытия дескриптора используется функция close
:
close(MYTEXT);
Если попытаться открыть файл, указав в качестве параметров функции open один из уже открытых дескрипторов, то вначале этот дескриптор закрывается, а затем повторно открывается.
Функции die
может передаваться список аргументов, которые будут выводиться вместо стандартного сообщения. Если сообщение не содержит символа перевода строки, то в его конец добавляется текст at имя_сценария line xxx, например:
die "Ошибка при открытия файла";
# Выводится сообщение " Ошибка при открытии файла at имя_сцевария line xxx"
die "Ошибка при открытии файла\п"
; # Выводится " Ошибка при открытии файла"
В Perl предусмотрена специальная переменная $!
, содержащая сообщение об ошибке, возникшей при выполнении последней системной операции (например, операции дискового ввода-вы вода). В числовом контексте конструкция $! возвращает мало что говорящий номер ошибки. В строковом контексте переменная $! возвращает сообщение операционной системы об ошибке:
open(MYFILE, "myfile") || die "Ошибка при открытии myfile: $!\n";
Если эта функция не сможет открыть файл из-за его отсутствия, будет выведено сообщение:
Ошибка при открытии myfile: a file or directory in the path does not exist.
Не используйте переменную $! для проверки успешности выполнения системной функции. Значение этой переменной определено непосредственно после выполнения системной операции (например, ввода-вывода) и только при неудачном завершении этой операции. Во всех остальных случаях переменная $! может иметь совершенно произвольное бессмысленное значение.
Иногда нужно вывести в программе предупредительное сообщение и продолжить ее выполнение. Для генерации предупреждений служит функция warn
, аналогичная die, за исключением того, что выполнение программы продолжается:
if {! open{MYFILE("output")) {
warn "Ошибка при открытии файла output: $!";
} else {
# Читаются данные файпа...
}
Perl существует несколько способов чтения файлов, определенных дескрипторами. Самый распространенный заключается в использовании оператора файлового ввода, называемого еще угловым оператором <>
. Для чтения информации из файла достаточно поместить его дескриптор в угловые скобки и присвоить это значение переменной, например:
open(MYFILE, "myfile") || die "Ошибка при открытии myfile: $!";
$line=
Угловой оператор в скалярном контексте читает одну строку из файла. Если файл заканчивается, угловой оператор возвращает значение undef. Строкой в текстовом файле называется последовательность символов, ограниченная специальным признаком конца строки. В UNIX таким признаком является символ перевода строки (ASCII-код 10), в DOS и Windows- последовательность символов возврата каретки и перевода строки (ASCII-коды: 13 и 10). Указатель стандартного признака конца строки может быть изменен в Perl, что позволяет добиться некоторых интересных эффектов.
Для чтения и вывода содержимого целого файла можно использовать следующий код (в примере предполагается, что MYFILE - открытый дескриптор файла):
while (defined($a=
print $a;
}
Для чтения информации из файла удобно использовать цикл while. Если в цикле while вместо условного выражения используется угловой оператор, Perl автоматически присваивает введенную строку специальной переменной $_ и повторяет цикл, пока файл не закончится:
while(
print $_;
}
При этом на оператор while возлагается присваивание введенной строки переменной $_ и проверка признака достижения конца файла. Такое интересное поведение случается только в цикле while и лишь тогда, когда условное выражение состоит из углового оператора. Не забывайте, что в прочитанных из файла данных, кроме самого текста, содержатся также символы конца строки. Если вам нужен только текст, используйте функцию chomp, позволяющую избавиться от символов конца строки.
В контексте списка угловой оператор читает файл целиком и присваивает его списку. Каждая строка файла присваивается соответствующему элементу списка или массива, как показано ниже:
open(MYFILE, "novel.txt") || die "$!";
@contents=
close(MYFILE);
В этом примере через дескриптор MYFILE считываются все данные из файла novel.txt и присваиваются массиву @contents. При этом первая строка файла novel.txt присваивается первому элементу массива @contents: $contents . Вторая строка присваивается $contents и т.д.
Для записи данных в файл сначала нужно открыть сам файл для записи. Синтаксис открытия файла для записи почти такой же, как и для чтения:
open (дескриптор, ">путь")
ореn (дескрвптор, ">>путь")
Синтаксис первой строки уже знаком нам, за исключением символа >
перед путем. Этот символ говорит Perl, что в файл, путь которого указан следом, должны быть записаны новые данные. При этом уже имеющиеся данные в файле стираются и указанный дескриптор открывается для записи. Во втором примере символы >>
говорят Perl, что файл открывается для записи, но если файл уже существует, новые данные дописываются после имеющихся. Вот примеры:
# Новые данные записываются поверх старых, если таковые есть
open(NEWFH, ">output.txt") || die "Ошибка при открытии output.txt: $!";
# Новые данные дописываются к уже существующим.
open(APPFH, ">>logfile.txt") || die " Ошибка при открытии logfile.txt: $!";
После окончания работы с файлом, открытым для записи, вопросом первостепенной важности становится закрытие этого файла и освобождение его дескриптора. Функция close сообщает операционной системе, что запись в файл завершена и данные должны быть помещены в место своего постоянного хранения на диске, например:
close(NEWFH);
close(APPFH);
После того как файл открыт для записи, поместить в него данные очень просто. Это делается с помощью хорошо вам знакомой функции print
. Пока функция print использовалась нами лишь для отображения данных на экране. В принципе эта функция может быть использована для записи данных в любой файл. Синтаксис оператора print, предназначенного для вывода данных в файл, очень прост:
print дескриптор СПИСОК
open (LOGF, ">>logfile") || die "$!";
if (! Print LOGF "Запись сделана ", scalar(localtime), "\n") {
warn "Ошибка при записи в файл logfile: $!";
}
close(LOGF);
Одновременно можно открыть сразу несколько файлов для чтения или записи, как показано в следующем примере:
open(SOURCE, "soursefile") || die "$!";
open(DEST, ">destination") || die "$!";
@contents=
В этом примере выполняется простое копирование файлов. Кстати, можно несколько сократить код, объединив в одном операторе операции чтения и записи:
print DEST
Так как функция print в качестве параметра ожидает передачи списка, оператор
При записи двоичных данных, таких как файлы GIF, EXE, документы MS Word и т.п., преобразование данных не требуется. Поэтому, чтобы ни Perl, ни операционная система не делали подобных преобразований, перед записью двоичных данных в файл необходимо использовать функцию binmode
. Она помечает дескриптор файла как дво-
ичный. Функция binmode вызывается после открытия файла, но до того, как будет выполнен ввод или вывод данных:
open(FH, "camel.gif") || die " $! " ;
binmode(FH); # Дескриптор становится двоичный.
После открытия файла функцию binmode к его дескриптору можно применить только один раз. При закрытии и повторном открытии двоичного файла придется заново вызвать функцию binmode. Использование binmode в системах, где не различаются текстовые и двоичные файлы (например, в UNIX), не вызывает никаких действий.
Перед тем как открыть файл, неплохо убедиться, что он действительно существует, проверить, не является ли он каталогом, и не приведет ли это к появлению сообщения об ошибке permission denied. В Perl имеются специальные операторы тестирования файлов
. Все они имеют похожий синтаксис:
-X дескриптор_файла
-X путь
Здесь X - конкретная операция тестирования, а дескриптор_файла - тестируемый дескриптор. Файл можно протестировать и без открытия дескриптора.
-r
| -г "file" | Истинное значение, если разрешено чтение "файла" |
-w
| -w $a | Истинное значение, если разрешена запись в файл, имя которого содержится в переменной $а |
-e
| -e "file" | Истинное значение, если "file" существует |
-z
| -z "file" | Истинное значение, если "file" существует, но он пустой |
-s
| -s "file" | Возвращает размер "file" в байтах, если тот существует |
-f
| -f "file" | Истинное значение, если "file" является обычным файлом (не каталогом) |
-d
| -d "catalog" | Истинное значение, если параметр "catalog" задает каталог |
-T
| -T "file" | Истинное значение, если параметр "файл" определяет текстовый файл |
-B
| -B "file" | Истинное значение, если параметр "файл" определяет двоичный файл |
-M
| -M "file" | Возвращает количество прошедших дней с момента последней модификации "файла" |
print "Где будем сохранять данные?";
$filename=
chomp $filename;
if (-s $filename) {
warn "Содержимое файла $file будет потеряно!\п";
warn "Он был модифицирован ",
-М $filename, "дней тому назад.\п";
}
Многие Perl-программисты имеют дело с текстовыми файлами, такими как конфиги или лог-файлы, поэтому, чтобы получить некоторые полезные знания, важно как можно раньше научиться работать с файлами.
Для начала давайте узнаем, как записывать данные в файл, так как это кажется наиболее простой задачей.
Прежде чем записывать в файл, нужно открыть его, то есть попросить операционную систему (Windows, Linux, OSX и т. д.) открыть канал, по которому ваша программа сможет "общаться" с файлом. Для этого в Perl есть функция
open
(с немного странным синтаксисом). use strict; use warnings; my $filename = "отчет.txt"; open(my $fh, ">", $filename) or die "Не могу открыть "$filename" $!"; print $fh "Мой первый отчет, сгенерированный с помощью perl\n"; close $fh; print "готово\n";Это хороший рабочий пример и мы к нему еще вернемся, но сперва давайте попробуем пример попроще:
Простой пример
use strict; use warnings; open(my $fh, ">", "отчет.txt"); print $fh "Мой первый отчет, сгенерированный с помощью perl\n"; close $fh; print "готово\n";Здесь тоже нужны кое-какие объяснения. Функция open принимает 3 параметра.
Первый, $fh , это скалярная переменная, которую мы объясляем в вызове open() . Мы могли бы объявить ее раньше, но обычно проще объявить ее прямо в вызове, хотя на первый взгляд это может выглядеть немного странно. Второй параметр определяет, каким образом мы открываем файл. В данном случае мы поставили знак "больше" (> ), что значит, что файл открывается для записи. Третий параметр - путь к файлу, который мы хотим открыть.
Когда эта функция вызывается, она присваивает переменной $fh специальный ключ, который называется указателем файла (file-handle). Нам не важно само содержимое этой переменной; в дальнейшем мы просто используем ее. Главное, обратите внимание, что содержимое файла по-прежнему находится на диске, и НЕ попадает в переменную $fh.
Когда файл открыт, мы можем использовать указатель $fh в выражении print() . Это выглядит почти так же, как print() в других частях нашего учебника, но в качестве первого параметра мы передаем указатель файла, и после него нет (!) запятой.
Этот вызов print() запишет текст в наш файл.
Затем в следующей строчке мы закрываем указатель на файл. Строго говоря, в Perl это не обязательно. Perl автоматически и корректно закроет все файловые указатели, когда переменная покинет область видимости, то есть, в крайнем случае, когда скрипт завершится. Но, так или иначе, явно закрывать файлы считается хорошей практикой.
Последняя строчка print "done\n" нужна только для того, чтобы пояснить следующий пример:
Обработка ошибок
Давайте возьмем предыдущий пример и заменим имя файла на несуществующий путь. Например:
Open(my $fh, ">", "некое_странное_название/отчет.txt");
Теперь, если запустить этот скрипт, мы увидим сообщение об ошибке:
Print() on closed file-handle $fh at ... готово
На самом деле, это просто предупреждение; скрипт продолжает выполняться, и поэтому мы увидим на экране слово "готово".
Более того, мы увидим предупреждение только потому, что явно запросили отображение предупреждений с помощью выражения use warnings . Попробуем закомментировать use warnings и увидим, что теперь скрипт молчит при неудачной попытке открыть файл. Так что вы даже не заметите этого, пока клиент, или, хуже того, ваш начальник, начнет жаловаться.
В любом случае, налицо проблема. Мы попытались открыть файл. Это не получилось, но мы все равно пытались туда что-то записать.
Лучше бы нам проверить, успешно ли сработал open() , прежде чем продолжать.
К счастью, вызов open() сам по себе возвращает TRUE в случае успеха и FALSE при отказе , так что мы можем сделать так:
Открой или умри (open or die)
open(my $fh, ">", "некое_странное_название/отчет.txt") or die;Это "стандартная идиома" open or die . Очень часто встречается в Perl.
die - это вызов функции, которая бросит исключение и таким образом завершит наш скрипт.
"open or die" это логическое выражение. Как вы знаете из предыдущей части учебника, "or" в Perl (как и во многих других языках) сокращается. Это значит, что если левая часть вернет TRUE, сразу понятно, что все выражение будет равно TRUE, так что правая часть вообще не выполняется. С другой стороны, если левая часть вернет FALSE, то правая часть выполнится, и результат ее выполнения и будет результатом всего выражения.
В данном случае мы используем эту особенность сокращения в нашеи выражении.
Если open() выполнится успешно, он вернет TRUE, и правая часть так и не выполнится. Скрипт просто перейдет к следующей строчке.
Если же open() не выполнится, он вернет FALSE. Тогда выражение справа от or тоже выполняется. Это приводит к исключению, и скрипт завершается.
В этом примере мы не проверяем итогового значения логического выражения, оно нам не нужно. Мы использовали это выражение только ради "побочного эффекта".
Если мы запустим скрипт с этим изменением, мы получим сообщение об ошибке:
Died at ...
и НЕ увидим "готово".
Улучшаем сообщение об ошибке
Вместо того, чтобы просто вызвать die без параметра, можно добавить некоторое объяснение того, что же произошло.
Open(my $fh, ">", "некое_странное_название/отчет.txt") or die "Не могу открыть файл "некое_странное_название/отчет.txt"";
Не могу открыть файл "некое_странное_название/отчет.txt" ...
Так-то лучше, но в какой-то момент кто-нибудь попробует поменять путь на корректную директорию...
Open(my $fh, ">", "корректная_директория_с_опечаткой/отчет.txt") or die "Не могу открыть файл "некое_странное_название/отчет.txt"";
Но сообщение об ошибке будет старым, потому что путь поменяли только в вызове open(), но не в сообщении.
Так что будет лучше использовать в качестве названия файла переменную:
My $filename = "некое_странное_название/отчет.txt"; open(my $fh, ">", $filename) or die "Не могу открыть файл "$filename"";
Теперь у нас правильное сообщение об ошибке, но мы по-прежнему не знаем, почему она произошла. Пойдем на шаг дальше, и используем $! - встроенную переменную Perl - чтобы выдать то, что нам сообщила система об ошибке:
My $filename = "корректная_директория_с_опечаткой/отчет.txt"; open(my $fh, ">", $filename) or die "Не могу открыть файл "$filename" $!";
Этот код выдаст
Не могу открыть файл "корректная_директория_с_опечаткой/отчет.txt" No such file or directory ...
Так гораздо лучше.
Ну а теперь вернемся к исходному примеру.
Больше?
Знак "больше" в вызове open может показаться непонятным, но если вы знакомы с перенаправлениями в командной строке, вы поймете, что он значит. А если нет, просто представьте, что это стрелка, показывающая направление потока данных: в файл справа.
Не латиница?
Если вам нужно работать с символами, не входящими в таблицу ASCII, вам стоит сохранять их в UTF-8. Чтобы это сделать, нужно сообщить Perl"у, что вы открываете файл в кодировке UTF-8.
Open(my $fh, ">:encoding(UTF-8)", $filename) or die "Не могу открыть файл "$filename"";
Две важнейших операции, касающиеся содержимого файлов - ввод (чтение) из файла и вывод (запись) в файл.
Для работы с содержимым файла программа должна его открыть . Открывая файл, мы указываем, что именно мы хотим с ним делать: читать или писать. Результатом открытия является так называемый файловый дескриптор - скалярное значение, хранящее сведения о текущем состоянии процесса чтения или записи. Операции чтения, записи и прочие операции ввода вывода в дальнейшем осуществляются через дескриптор.
Открывается файл при помощи встроенной процедуры open . Процедура получает три параметра: переменную для создаваемого дескриптора, строку, обозначающую режим открытия (чтение, запись или что-то ещё), и, наконец, имя файла:
Perl
my $file ; open $file , "<" , "MyFile.txt" ; открытие для чтенияМы обожаем совмещать объявление переменной для дескриптора с вызовом процедуры open:
Perl
open my $file , "<" , "MyFile.txt" ;Значение "<" , переданное как второй параметр, символизирует чтение. Знак «меньше», повернувшийся спиной к имени файла, как бы намекает нам, что готовится извлечение информации из файла. Угадайте, как открыть файл для записи? Конечно же так:
Perl
open my $file , ">" , "MyFile.txt" ; открытие для записиВ примерах использования процедуры open указывалось относительное имя файла - по отношению к директории, являющейся для данной программы текущей. Сразу после запуска программы это та директория, из которой запущена программа. Но текущая директория может быть изменена процедурой chdir:
Perl
chdir "/" ;Нет никаких противопоказаний к тому, чтобы задавать при открытии абсолютное имя файла:
Perl
open my $passwd , "<" , "/etc/passwd" ;В некоторых операционных системах, в полных именах файлов применяется другой разделитель директорий. Например, в Microsoft DOS и Microsoft Windows вместо слэша / применяется разделитель бэкслэш \ . Тем не менее, задавая полное имя в программе на Perl при открытии, следует использовать слэш: "/c:/dos/autoexec.bat" .
Интуиция подсказывает нам, что всё, что открывается, должно быть рано или поздно закрыто. Это верно и для файлов. Любой сценарий ввода/вывода устроен одинаково: открытие, собственно ввод или вывод, и, наконец, закрытие
Закрытие файла освобождает ресурсы операционной системы, занятые при открытии, и гарантирует, что данные, записанные в файл, дойдут по назначению. После закрытия переменная-дескриптор теряет актуальность и не может быть использована для ввода/вывода.
Для закрытия файлов служит встроенная процедура close:
Perl
close $file ;Не стоит ожидать, что открытие файла всегда будет успешным. Есть много причин, которые могут помешать. Среди них:
- отсутствие нужного файла;
- отсутствие прав на чтение или запись;
- неполадки с носителем: испорченный диск или флешка, перебои с сетью в случае сетевых файлов.
В этих и подобных случаях все дальнейшие операции с файлом потеряют всякий смысл и лишь приведут Perl в ярость. Однако процедура open в своём возвращаемом значении сообщает об итоге операции: при успехе возвращает истинное значение, а при неудаче - ложное. Хорошо написанная программа должна проверять это значение, и действовать в зависимости от него. Например, при неудачном открытии завершить работу программы с выдачей соответствующего сообщения:
Perl
if (open … ) { работа с файлом и последующее закрытие } else { die ; }Обратите внимание на специальную переменную $! . В случае возникновения ошибки в эту переменную автоматически помещается текст, объясняющий причину ошибки, например, Нет такого файла или каталога или Отказано в доступе или что-то ещё. Текст в $! , в зависимости от системных настроек, может быть на другом языке.
Гораздо изящней выглядит следующая идиома:
Perl
open … or die "Невозможно открыть файл: $!\n" ;В этом логическом выражении два операнда. Если первый (то, что возвращает open) принимает истинное значение, то в вычислении второго нет нужды, так как всё выражение уже заведомо истинно. Если же open возвратит ложное значение, то значение всего выражения определяется по второму операнду, который в этом случае должен быть вычислен. Для вычисления будет вызвана процедура die со всеми вытекающими последствиями.
Конечно, программа не обязана завершать работу при неудачном открытии файла. Например, если предполагается обработка нескольких файлов, можно просто перейти к следующему, сообщив об ошибке при помощи процедуры warn:
Perl
for (@fileNames ) { open my $file , "<" , $_ or warn "Невозможно открыть файл $_: $!" and next ; работа с очередным файлом и последующее его закрытие }Имеется два способа чтения из дескриптора: побайтное/посимвольное и построчное.
При побайтном/посимвольном чтении файла программа запрашивает у открытого дескриптора очередную порцию данных нужного размера и предоставляет скалярную переменную для запрошенных данных. Для чтения служит встроенная процедура read:
Perl
read $file , $buffer , 16 ;Можно представлять себе файл, открытый для чтения, как последовательность байтов. Воображаемый указатель отделяет уже прочитанную часть последовательности от ещё непрочитанной. Операция чтения приводит, помимо прочего, к сдвигу указателя к концу файла. За счёт этого следующая команда чтения получит доступ к новой порции данных. Дескриптор файла хранит в себе разнообразную информацию об открытом файле, и, в том числе, этот указатель - номер первого непрочитанного байта. Сразу после открытия указатель равен нулю.
Что же будет, если запросить при чтении больше байт, чем размер непрочитанной части файла? Ничего страшного, компьютер не сломается. Просто в переменную-буфер отправится меньше байт, чем было запрошено. Контролировать это явление удобно, пользуясь возвращаемым значением процедуры read - это количество байт, которое удалось прочесть. Широко распространена при программировании на Perl такая идиома:
Perl
while (read $file , $buffer , $size ) { сделать что-то с $buffer }Здесь значение, возвращённое из read , используется в качестве условия цикла. Рано или поздно файл будет прочитан до конца, и следующий вызов read возвратит ноль (ложное значение). Это прервёт цикл, что нам, собственно, и нужно.
В отличие от побайтного/посимвольного чтения, когда запрашивается заданное количество байтов или символов, при построчном чтении размер прочитанного заранее не оговаривается. Вместо этого считывается строка - последовательность байтов или символов вплоть до символа или символов, обозначающих конец строки.
Построчное чтение осуществляется оператором <…> . Код <$file > приводит к считыванию очередной строки из дескриптора $file в переменную по умолчанию $_ .
Для последовательной обработки всех строк файла удобно использовать цикл while . Например, программа, печатающая содержимое файла на экран, строка за строкой, могла бы выглядеть так:
Perl
open my $file , "<" , "file.txt" or die "Невозможно открыть файл: $!\n" ; while (<$file >) { print ; }Здесь читатель справедливо задаётся вопросом: что именно печатает процедура print в теле цикла? Переменную по умолчанию, конечно. Можно было бы написать print $_ , но вряд ли это добавит ясности.
Другой пример. В уже открытом файле записаны числа, по одному в строке. Требуется вывести на экран их сумму.
Perl
my $sum =0 ; while (<$file >) { chomp ; $sum +=$_ ; } print "$sum\n" ;Команда chomp необходима вот по какой причине. Оператор <…> вместе со строкой считывает и завершающий строку символ, который создаст проблему, если считанная строка впоследствии будет участвовать в арифметическом выражении. Встроенная процедура chomp удаляет этот символ, если строка заканчивается им. Если же последний символ другой, процедура ничего не делает. такая предосторожность нужна на тот случай, если, к несчастью, файл не заканчивается символом конца строки. Тогда и последняя прочитанная из файла строка закончится чем-то другим. Имеется также процедура chop , которая удаляет и возвращает последний символ строки независимо от того, какой он. Обе процедуры, chop и chomp , работают со строкой, переданной как параметр, но в отсутствие параметра - с переменной $_ .
Не следует думать, что с оператором построчного чтения мы обречены на использование переменной $_ . Если требуется читать в другую переменную, используем присваивание:
Perl
$s =<$file >;Самое время сообщить об одной особенности оператора построчного чтения. Его смысл немного меняется, если оператор поместить в списочный контекст, то есть туда, где должен быть список. Например, если присвоить массиву:
Perl
@s =<$file >;В этом случае все строки, прочитанный из файла, заполнят массив. Ещё можно организовать переборный цикл:
Perl
print for <$file >;Этот код, как и приведённый выше цикл while , печатает строки файла на экран. К тому же результату приведёт код
Perl
print <$file >;(здесь пропущено слово for ). Но мы не рекомендуем такой подход, поскольку здесь сначала прочитываются все строки из файла, а затем передаются как список параметров в процедуру print . При этом все строки без всякой необходимости занимают память, а при большом файле объём памяти может быть очень велик.
Вообще, построчное чтение может создать похожую проблему, если в файле имеются очень длинные строки. И хотя это не характерно для текстовых файлов (например, созданных в текстовом редакторе), следует принимать это обстоятельство во внимание.
При открытии файла для чтения предполагается, что файл существует, в противном случае открытие приводит к ошибке. Если файл открывается для записи, требовать существования файла уже не стоит: отсутствующий файл создаётся пустым. Если же файл существовал, всё его содержимое уничтожается, и мы снова получаем пустой файл.
Для записи в дескриптор применяется давно знакомая нам процедура print , но не совсем так, как мы привыкли:
Perl
print $file $string ;Здесь содержимое строки $string записывается в открытый дескриптор $file . Обратите особое внимание на отсутствие запятой после первого параметра $file . С запятой смысл команды будет другим: в дескриптор ничего не запишется, а вместо этого программа выведет на экран строковое представление значений обеих переменных $file и $string:
GLOB(0x989e830)Привет!
(шестнадцатеричное число в скобках, скорее всего, будет другим, ну и вместо слова Привет! может оказаться другой текст). Число это нам ни о чём не говорит, как и загадочное слово GLOB вместе со скобками. Итак, если все параметры процедуры print разделены запятыми, все они печатаются на экран. Если после первого параметра нет запятой, этот параметр должен быть дескриптором, открытым для записи, а остальные параметры записываются в него:
Perl
print $file "Hello, " , $user ;Можно все параметры после дескриптора заключить в скобки:
Perl
print $file ("Hello, " , $user );Один из видных специалистов по языку Perl рекомендует заключать дескриптор в фигурные скобки, чтобы зрительно отделить его от остальных параметров:
Perl
print {$file } "Hello, " , $user ;Программисты часто встречаются с ситуацией, когда требуется в шаблонный текст в определённых местах поместить изменяющиеся фрагменты. Perl хорошо приспособлен для решения таких задач. Изменяющиеся части текста очень удобно поместить в переменную, а имя переменной вставить в нужное место в строку, заключённую в двойные кавычки:
Perl
print "Уважаемый $name! Вы задолжали $duty рублей. Уплатите до $date, иначе $punishment.\n" ;Таким способом можно вставлять в шаблон и строки, и числа. Что касается чисел, в каких-то ситуациях может потребоваться их особое форматирование. Числа перед включением в шаблон может потребоваться округлить до нужного количества десятичных цифр после точки, дополнить слева нужным количеством нулей или пробелов, чтобы число хорошо смотрелось в таблице. Может потребоваться вставить число в двоичном или шестнадцатеричном формате.
В таких ситуациях на помощь приходят встроенные процедуры printf и sprintf . Первый параметр в обеих процедурах - так называемая форматная строка . Это шаблонный текст, в который с определённые места должны быть вставлены остальные параметры. Места вставки параметров и желательный формат специальным образом помечаются. Метка представляет собой знак процента, за которым следует обозначение формата. Остальные параметры процедуры вставляются на места форматных меток в соответствующем формате. Процедура printf выводит результат в дескриптор подобно процедуре print , а sprintf возвращает полученную после всех вставок строку для какого-то иного использования. В принципе, если бы не было printf , её можно было бы легко запрограммировать: вызов printf (…) равносилен print (sprintf (…)) .
Теперь подробнее о форматных строках и форматных метках.
Шестнадцатеричный формат с дополнением нулями до трёх цифр:
Использование знака процента как признака метки лишает нас возможности использовать его в форматной строке как таковой. Эта трудность не новая для нас, и разрешается она уже хорошо знакомым способом. Комбинация из двух знаков процента означает одиночный знак процента (подобно тому, как \\ внутри "" -строк означает один бэкслэш):
Perl
$p =38 ; $x =43 ; printf "%d%% от %d равно %d\n" , $p , $x , $p /100 *$x ;38% от 43 равно 16
Точно так же, как и print , процедура printf может осуществлять вывод не только на экран, но и дескриптор файла, открытого для записи или для добавления:
Perl
printf {$file } … ;В Perl реализовано два набора функций для осуществления операций чтения/записи в файл. Функции одного набора используют буфер - некоторую область памяти - для накопления читаемой/записываемой в файл информации, после заполнения которого или закрытия файла осуществляется физическая запись данных буфера на диск или их пересылка в программу. Эти функции, к которым относятся print , readline, <>, read , getc , seek и tell , по существу, представляют собой интерфейсные функции к процедурам буферизованной библиотеки ввода-вывода stdio языка С. Использование буферизованных операций ввода-вывода ускоряет чтение/запись данных в файлы. Функции второго набора, к которым относятся sysread, syswrite и sysseek, обращаются непосредственно к функциям ввода-вывода операционной системы, осуществляя прямые физические операции чтения/записи данных без накопления их в промежуточном буфере. Открывать файл для доступа как буферизованными, так и небуферизованными функциями можно любой из двух функций - open () или sysopen() - при открытии файла не регламентируется, каким набором функций следует обрабатывать содержащуюся в нем информацию. Единственное требование заключается в том, что не рекомендуется смешивать эти два подхода для одного файла в одном сеансе открытия, так как это может привести к непредсказуемым результатам.
ВНИМАНИЕ При работе с одним и тем же файлом не следует смешивать вызовы буферизованных и небуферизованных функций ввода-вывода. Подобная практика может приводить к непредсказуемым коллизиям. Если требуется, например, использовать небуферизованных функции чтения/записи, а информация из файла уже читалась буферизованной операцией о, то следует закрыть файл, снова его открыть и использовать для работы с ним небуферизованные функции.