Объявления и комментарии
Объявления и комментарии
Программа Perl представляет собой последовательность операторов и объявлений, которые обрабатываются интерпретатором в том порядке, как они появляются в тексте программы. Во многих языках программирования обязательны объявления всех используемых переменных, типов и других объектов. Синтаксис Perl является "демократичным" в этом отношении и предписывает обязательные объявления только форматов и подпрограмм.
Объявления форматов и подпрограмм являются глобальными, а это означает, что они "видимы" из любого места сценария. Объявления могут располагаться в программе в любом месте, куда можно поместить оператор, но обычно их размещают либо в начале, либо в конце программы, чтобы быстро найти и откорректировать в случае необходимости. Объявления обрабатываются во время компиляции и не влияют на выполнение последовательности операторов, когда откомпилированный во внутренний код интерпретатора сценарий начинает свою работу.
В Perl нет специального оператора объявления переменной, она определяется при первом ее использовании, причем с помощью ключа - w интерпретатора можно задать режим отображения предупреждающих сообщений при попытке использования не инициализированной переменной. Переменные можно определять как глобальные, видимые из любой точки программы, так и с помощью функции ту как локальные, видимые в определенной части программы — блоке.
(Объявления локальных переменных описываются и )
Можно объявить подпрограмму с помощью оператора sub , не определяя ее, т. е. не задавая операторы, реализующие ее функцию. После такого объявления подпрограммы ее имя можно использовать как операцию, действующую на список, определяемый передаваемыми ей параметрами.
(Более подробно объявления и определения подпрограмм рассматриваются )
Как уже отмечалось выше, если интерпретатор встречает символ #, то он игнорирует любой текст, расположенный за ним. Такая конструкция называется комментарием и ее действие распространяется до конца строки после символа #. Комментарии используются для документирования программы и не следует ими пренебрегать, так как они вносят ясность в понимание того, что выполняет программа. Однако и злоупотреблять ими не следует, так как слишком большое их количество может ухудшить читаемость программы — одну из важных характеристик любой программы.
Комментарии располагаются в любом месте программы. Их можно разместить непосредственно после оператора в той же самой строке, как в нашем примере 2.1, или занять ими всю строку, если первым символом в ней является символ комментария #. Если необходимо временно исключить из потока выполняемых операторов какой-либо из них, то его можно просто закомментировать, не забыв, правда, удалить символ комментария, когда этот оператор снова надо будет включить в поток вычислений.
Простая программа
Простая программа
Изучение любого языка программирования начинается с его синтаксиса, одну из неотъемлемых частей которого составляет описание структуры программы, определяющей состав и порядок расположения разнообразных конструкций в теле программы. Мы не будем отступать от сложившихся традиций и объясним необходимые понятия на примере простой программы Perl, получающей информацию от пользователя и в ответ печатающей на экране монитора приветствие.
Язык Perl — достаточно простой язык программирования, семантика ключевых слов которого соответствует их значению в английском языке, поэтому даже начинающий его изучение программист, во всяком случае, так утверждают его разработчики, без особого труда может разобраться в простой программе Perl. Ну, что ж, может быть так оно и есть, но, как говорится, "лучше один раз увидеть, чем сто раз услышать". Итак, в примере 2.1 приведен текст программы, которая печатает на экране монитора приглашение ввести имя пользователя, а в ответ просто приветствует его.
Пример 2.1. Простая программа-приветствие
01 #! /bin/usr/perl
02
03 print "Ваше имя?\n"; # Приглашение ввести имя.
04 $name = <STDIN>; # Ввод имени с клавиатуры.
05
06 $~ = NAME_FORMAT; # Назначение формата вывода.
07 write; # Вывод приветствия.
08
09 $~ = NAME_FORMAT_BOTTOM; # Вывод нижней разделительной черты.
10 write;
11
12 format NAME_FORMAT= # Начало описания формата.
13 Привет, @»»»»»»! # Строка вывода.
14 $name # Переменная, значение которой
# подставляется в строку вывода.
15 . # Завершение описания формата.
16
17 format NAME_FORMAT_TOP= # Заголовок формата NAME_FORMAT.
18 ================================
19 Сообщение Perl-программы
20
21 .
22
23 format NАМЕ_FОRМАТ_TОP= # Формат вывода нижней разделительной черты
24 ================================
25 .
Эта программа не совсем типичная для Perl-программы — в ней отсутствуют операторы ветвления, цикла, определение собственных подпрограмм и их вызов и многое другое, но она все же отражает предназначение языка Perl как языка генерирования отчетов, осуществляя вывод приветствия в соответствии с технологией создания отчетов путем определения формата вывода, который в большинстве современных языков либо отсутствует вообще, либо практически не используется программистами.
Если читатель по тексту программы уже понял, как она будет работать (надеемся, что наши комментарии помогли ему в этом), то теперь самое время проверить его догадку — выполнить эту программу. Но прежде следует в обычном текстовом редакторе набрать ее текст и сохранить в файле с расширением pl. Программы Perl являются обычными текстовыми файлами и для их создания можно использовать любой текстовый редактор: в UNIX, например, всегда доступные vi и ed, а в Windows — Блокнот (notepad.exe) или редактор программы Far.
Замечание
Обычно расширение файла (до трех символов) используется для идентификации файлов определенной группы: выполнимые файлы программ (ЕХЕ), файлы текстового процессора Word (DOC) и т. д. Для сценариев Perl принято использовать двухбуквенное расширение pl.
Для выполнения программы примера 2.1, сохраненной в файле programl.pl, в любой операционной системе, имеющей командную строку, достаточно набрать в ней команду:
perl program1.pl
В результате выполнения программы на экране монитора появится приглашение ввести имя и после ввода имени отобразится приветствие Perl-
программы. Дамп экрана после выполнения нашей программы можно увидеть в примере 2.2, где мы полужирным шрифтом выделили ввод пользователя.
Пример 2.2. Вывод программы примера 2.1
Ваше имя?
Александр
==========================
Сообщение Perl-программы
Привет, Александр!
==========================
Соответствует ли он вашим соображениям относительно работы этой программы при анализе ее текста? Если да, то мы вас поздравляем, если нет — не надо отчаиваться, несколько наших комментариев поставят все на свои места.
Первый оператор — это специальный комментарий, который для системы UNIX определяет местонахождение интерпретатора perl. Дело в том, что в этой операционной системе можно сделать любой файл выполняемым с помощью команды
chmod +x programl.pl
и запустить на выполнение из командной строки:
./programl.pl
В этом случае первая строка программы сопоставляет файлу приложение, которое должно быть загружено для его обработки. Более того, в ней можно определить при необходимости параметры, или ключи, определяющие режим работы приложения, в нашем случае интерпретатора perl. Можно задать отображение предупреждающих сообщений или загрузку отладчика в случае обнаружения серьезной ошибки с помощью следующей строки:
#! /bin/usr/perl -w -d
(О режимах работы интерпретатора perl и соответствующих ключах см.)
Замечание
При работе в операционной системе Windows можно ассоциировать с расширением файла определенную программу, которая будет вызываться при двойном щелчке мышью на любом файле с таким расширением. Программа установки интерпретатора языка Perl фирмы ActiveState Tool Corp., который называется Active Perl, автоматически устанавливает соответствие файлов с расширением рl и интерпретатора языка Perl. При двойном щелчке на файле сценарий действительно выполняется в окне DOS, которое, однако, автоматически закрывается после выполнения последнего оператора, что не позволяет просмотреть отображенные в нем результаты. Чтобы этого не происходило, следует заменить строку вызова приложения, генерируемую программой установки, на следующую:
C:\WINDOWS\Dosprmpt.pif /Полный__путь\perl.exe
Для этого следует выполнить команду Параметры меню Вид программы Проводник, в диалоговом окне Параметры на вкладке Типы файлов в списке Зарегистрированные типы выделить Perl File и нажатием кнопки Изменить отобразить диалоговое окно Изменение свойств типа файлов. В этом окне в списке Действия выбрать Open и нажать кнопку Изменить. В появившемся диалоговом окне Изменение действия для типа: Perl File в поле Приложение, исполняющее действие: ввести указанную строку, задав в ключе /с полный путь к папке, где расположен двоичный исполняемый файл интерпретатора Active Perl, который называется perl.exe.
При работе с интерпретатором Active Perl нет необходимости задавать первую строку, однако если необходимо установить режимы работы интерпретатора, то ее следует задать, причем не обязательно указывать полный путь расположения интерпретатора, достаточно только его имя perl и необходимые ключи.
Внимание
Все примеры программ в этой книге проверены в интерпретаторе Active Perl 520 для Windows 95/NT и встроенном интерпретаторе Perl системы Linux RedHat 6.1, соответствующих текущей стабильной версии Perl 5.005.
Вообще любой комментарий в языке Perl начинается с символа "#" и распространяется до конца строки. Единственное исключение — это если сразу же после символа комментария следует символ "!".
Внимание
В строке специального комментария не следует вводить ничего, кроме пути местонахождения интерпретатора и его ключей. В противном случае можно получить сообщение об ошибке.
В строке 3 функция print посылает на системное устройство вывода (обычно это монитор компьютера) содержимое списка своих параметров, при необходимости преобразуя его в символьное представление. В нашей программе при выполнении этого оператора на экране монитора отобразится содержимое строки, передаваемой функции print в качестве параметра. Для тех, кто не знаком с языком С, последовательность символов "\n" может показаться странной, тем более, что в дампе экрана (см. пример 2.2) она даже не отображается. Это одна из так называемых управляющих или ESC-
последовательностей, которые предназначены для представления неотображаемых символов — в. данном случае символа перехода на новую строку.
Оператор строки 4 ожидает ввод со стандартного устройства ввода (обычно это клавиатура) и присваивает переменной $name введенное пользователем имя. Забегая вперед, скажем, что при работе с файлами в Perl определяются дескрипторы файлов, представляющие собой обычные идентификаторы, используемые в программе для ссылки на файлы, с которыми они ассоциированы. В языке имеется несколько предопределенных дескрипторов файлов, одним из которых является STDIN
, ассоциированный со стандартным устройством ввода. Операция о выполняет ввод из файла, заданного своим дескриптором.
Оператор строки 6 назначает системной переменной $~ имя используемого формата для выполнения вывода на стандартное устройство вывода системной функцией write из строки 7. Сам формат задается в строках 12-15 оператором
format , в котором определяется имя формата (
NAME_FORMAT ), завершающееся символом равенства "=". В последующих строках до строки 15, содержащей единственный символ точка ".", фиксирующий завершение объявления формата, задается сам формат, который обычно представляет собой повторяющуюся последовательность двух строк: строки вывода и строки, перечисляющей через запятую переменные, чьи значения при выводе подставляются вместо поледержателей, объявленных в строке вывода. Строка вывода представляет собой именно строку, которая выводится функцией
write в определяемый ее параметром файл (если параметр не задан, то вывод осуществляется на стандартное устройство вывода). В этой строке значащими являются все пробельные символы (сам пробел, символы перехода на новую строку и страницу, символ табуляции). Поледержатель — это специальная конструкция в строке вывода, начинающаяся с символа "@", за которым следует определенное количество специальных символов, задающих длину поля, в которое выводится значение переменной, ассоциированной с поледержателем и определенной в списке переменных, задаваемом в строке, непосредственно следующей за строкой вывода. Если в строке вывода поледержателей нет, то строку со списком ассоциированных с ними переменных задавать не надо.
В формате NAME_FORMAT определена одна строка вывода с одним поледержателем, который резервирует поле длиной в 12 символов и определяет, что выводимое значение должно быть прижато вправо (символ ">"). Это означает, что если значение ассоциированной с этим поледержателем переменной $name будет меньше 12 символов, то в этом поле при выводе они будут выровнены по правому краю. Если выводимое значение преобразуется в строку длиной более 12 символов, она обрезается по правому краю, т. е. отображаются первые 12 символов, считая слева направо.
Если для формата определен формат с таким же именем и суффиксом _ТОР, то этот формат определяет вид заголовка страницы, который будет отобра
жаться всякий раз при выводе новой страницы с использованием основного формата. Для формата
NAME_FORMAT определен формат заголовка в строках 17-19.
Завершается вывод приветствия печатанием разделительной черты функцией write строки 10 с использованием формата NAME_FORMAT_BOTTOM. Обратите внимание, что для использования нового формата вывода нам пришлось изменить значение системной переменной $~. Это следует делать всякий раз, когда необходимо организовать вывод с помощью нового формата.
Итак, мы разработали и выполнили нашу первую программу на языке Perl. Теперь пришло время обратиться к синтаксису языка и рассказать в самых общих чертах, из чего состоит программа на языке Perl, т. е. описать структуру программы.
и какими элементами они представлены
Вопросы для самоконтроля
Что такое лексемы и какими элементами они представлены в языке Perl?
Что такое выражение и зачем оно создается?
Чем выражение отличается от оператора?
Каковы различия между объявлением и оператором?
Какие классы операторов имеются в языке Perl?
Что представляет собой Peri-программа?
Упражнения
Модифицируйте программу примера 2.1, чтобы она всегда приветствовала весь мир, т. е. при ее выполнении всегда отображалось бы "Привет, мир!".
Модифицируйте программу примера 2.1, использовав для вывода оператор print , имеющий следующий синтаксис: print выражение1, выражение2, . . . , выражение n;
Исправьте ошибку в программе: #! /usr/bin/perl Это строка показывает местонахождение интерпретатора print "Perl";
Исправьте ошибки в программе:
i! /usr/bin/perl -w $write = 24
$two =3
$rez = $write * $two
Исправьте ошибку в программе:
'#! /usr/bin/perl -w $write = 24;
# Печать переменной! print $write;
Выражения и операторы
Выражения и операторы
Оператор —
это часть текста программы, которую интерпретатор преобразует в законченную инструкцию, выполняемую компьютером. С точки зрения синтаксиса языка (способов составления правильных конструкций, распознаваемых интерпретатором) оператор состоит из
лексем
— минимальных единиц языка, которые имеют определенный смысл для интерпретатора. Под минимальной единицей языка понимается такая его единица, которая не может быть представлена более мелкими единицами при дальнейшем ее синтаксическом разборе. В языке Perl лексемами могут быть идентификаторы, литералы, знаки операций и разделитель.
Мы дадим определения всем допустимым в языке лексемам. Хотя их семантика (смысл) может оказаться для начинающих программистов и не совсем ясна, но мы вернемся к некоторым определениям в последующих главах, где уточним их и синтаксис, и семантику в связи с вводимыми элементами языка. Дело в том, что, к сожалению, невозможно описать язык без ссылок вперед.
Идентификатор —
это последовательность букв, цифр и символа подчеркивания "_", начинающаяся с буквы или подчеркивания и используемая для именования переменных, функций, подпрограмм, дескрипторов файлов, форматов и меток в программе. Программист может использовать любые правиль
ные идентификаторы для именования перечисленных объектов программы, если только они не совпадают с
ключевыми словами
языка — предопределенными идентификаторами, которые имеют специальное значение для интерпретатора языка Perl, например if, unless, goto и т. д. Примеры правильных и неправильных идентификаторов представлены в примере 2.3.
Пример 2.3. Правильные и неправильные идентификаторы
# Правильные идентификаторы
myNamel
my_Namel
_myName__l
# Неправильные идентификаторы
1
myName # Начинается с цифры.
-myName # Начинается не с символа буквы или подчеркивания.
my%Name # Используется недопустимый для идентификаторов символ
my
# my является зарезервированным словом.
Замечание
Забегая вперед, скажем, что так как имена переменных Perl начинаются со специального символа ("
$ ", "
@ ", "
% "), определяющего их тип, после которого следует идентификатор, то в этом случае использование идентификатора, совпадающего с ключевым словом Perl, является правомочным и не вызывает ошибку интерпретатора. Так, следующие имена переменных являются допустимыми:
$print ,
@do ,
%if , однако подобная практика не рекомендуется. Это замечание не относится к идентификаторам, используемым для именования дескрипторов файлов и меток, имена которых не начинаются с определенных символов.
(Как используются идентификаторы для объявления переменных см. )
(Как используются идентификаторы в дескрипторах файлов см. )
(Как используются идентификаторы для объявления форматов см. )
Литерал,
или буквальная константа, — символ или слово в языке программирования, определяющие в отличие от переменной свое собственное значение, а не имя другого элемента языка. Буквальные константы тесно связаны с типами данных, представимыми в языке, и являются, собственно говоря, их представителями. В Perl литералами являются числа и строки.
123 # Целое число.
23.56 # Вещественное число с фиксированной точкой.
2Е+6 # Вещественное число с плавающей точкой.
"Язык Perl" # Строковый литерал.
(О литералах см. в )
Знаки операций —
это один или более специальных символов, определяющих действия, которые должны быть выполнены над величинами, называемыми операндами. Выполняемые действия называются операциями, которые могут быть унарными (применяются к одному операнду), бинарными (применяются к двум операндам) и тернарные (участвуют три операнда).
Пример 2.5. Операции языка Perl
++$п; # Унарная операция (++)
23 * $п; # Бинарная операция (*)
$п >= 3 ? print "true" : print "false"; # Тернарная операция (?:)
(Об операциях и используемых знаках операций см. в )
Разделитель —
это символ ";", которым завершается любой оператор и который сообщает об этом интерпретатору. Использование разделителя позволяет на одной строке задавать несколько операторов, хотя это и не принято в практике программирования, так как ухудшает читаемость текста программы.
В операторе все его лексемы могут отделяться любым числом
пробельных символов,
к которым относятся сам пробел, знак табуляции, символ новой строки, возврат каретки и символ перехода на новую строку. Поэтому один оператор можно записать на нескольких строках и для этого не надо использовать никакого символа продолжения, требующегося в других языках программирования. Например, оператор номер 4 присвоения данных, введенных с клавиатуры, из примера 2.1 можно записать и так:
Пример 2.6. Использование пробельных символов в операторе
$name
= <STDIN>
;
Можно вообще не использовать пробельные символы в операторе, но для обеспечения читаемости программы мы рекомендуем отделять лексемы одним пробелом, тем более что могут встречаться ситуации, когда интерпретатор не однозначно выделяет лексемы из непрерывного потока символов.
Замечание
Так как пробельные символы не являются значащими в Perl, то обычно их используют для структуризации текста программы, которая заключается в написании некоторой группы операторов, логически подчиненных некоторой конструкции языка, с некоторым отступом относительно этой конструкции. Например, подобный подход можно применить к блоку операторов, выполняющихся в конструкции цикла, сдвинув все их вправо относительно этой конструкции. Структуризация текста программы способствует ее лучшему прочтению и широко практикуется программистами многих языков программирования, например языка С.
Операторы в языке Perl могут быть простыми или составными.
Простой оператор —
это выражение, завершающееся разделителем точкой с запятой ",-", и которое вычисляется исключительно ради своего побочного эффекта. Что такое побочный эффект выражения мы определим немного позже, а сейчас остановимся на понятии "выражение".
Выражение —
последовательность литералов, переменных и функций, соединенных одной или более операцией, которые вычисляют скаляр или массив, т. е. при обработке интерпретатором выражения единственным действием является вычисление значения, а не выполнение некоторых других действий, например присвоение переменной нового значения. При вычислении выражения могут проявляться побочные эффекты, когда при вычислении выражения меняется значение переменной, входящей в выражение. Они могут вызываться, например, операциями увеличения (++) и уменьшения (--) или при вызове функции, которая изменяет значение своего фактического параметра. Как упоминалось выше, простой оператор и выполняется, чтобы реализовать этот побочный эффект, иначе какой смысл просто вычислить выражение, значение которого никоим образом нельзя использовать.
Пример 2.7. Простые операторы
++$n; # Значение переменной $n увеличивается на единицу.
123*$n; # Простой оператор без побочного эффекта.
Каждый простой оператор может иметь модификатор, который располагается после выражения перед завершающей точкой с запятой. Модификаторы представляют собой ключевые слова if , unless , while и until , за которыми следует некоторое выражение. Семантика использования модификатора заключается в том, что простой оператор выполняется, если истинно или ложно выражение, стоящее после модификатора, в зависимости от используемого модификатора. Модификаторы употребляются так же, как и в обычном разговорном английском языке. Например, простой оператор
$n++ while <STDIN>;
будет выполняться, увеличивая всякий раз значение переменной $n на единицу, пока пользователь будет осуществлять ввод с клавиатуры. Остановить выполнение этого оператора можно вводом комбинации клавиш <Ctrl>+<Z>ium <Ctrl>+<C>.
Замечание
Язык Perl вобрал в себя лучшие элементы других языков программирования, в основном С. Конструкция модификаторов заимствована из умершего языка BASIC/PLUS фирмы Digital Equipment Corp.
(Подробно все модификаторы простых операторов рассматриваются в )
Чтобы определить конструкцию, называемую составным оператором, нам придется сначала ввести понятие "блок". Последовательность операторов Perl, определяющая область видимости переменных, называется
блоком.
После знакомства с переменными это определение не будет таким туманным, каким оно может показаться сейчас начинающему программисту. Для целей этой главы достаточно мыслить блок как последовательность операторов, заключенную в фигурные скобки:
{ оператор_1;
оператор_n; }
Составной оператор определяется в терминах блока и может быть одного из следующих видов:
Пример 2.8. Составные операторы
if (выражение)
БЛОК
if (выражение)
БЛОК_1
else
БЛОК__2
if (выражение_1)
БЛОК_1
elsif (выражение_2)
БЛОК_2 ...
else
БЛОК_п
МЕТКА
while (вьражение)
БЛОК
МЕТКА
while (вьражение)
БЛОК_1
continue
ЕЛОК_2
МЕТКА
for (вьражение_1; выражение_2; аьражение_3)
БЛОК
МЕТКА
foreach
переменная (список) БЛОК .МЕТКА БЛОК_1
continue
БЛОК_2
Обратим внимание читателя на то, что, в отличие от языков программирования С и Pascal, составные операторы Perl определяются в терминах блоков, а не в терминах операторов. Это означает, что там, где нужен блок, он всегда должен задаваться с помощью фигурных скобок. В составных операторах, если даже блок состоит из одного оператора, он должен быть заключен в фигурные скобки. Такой синтаксис не приводит к двусмысленностям и, например, во вложенных операторах условия всегда ясно, с каким if
согласуется
else
или
elsif
. Метка, представляющая собой идентификатор с двоеточием ":", в составных операторах не обязательна, но если она присутствует, то имеет значение для операторов управления циклами
next ,
last и
redo .
(Подробно все составные операторы рассматриваются в )
В этой главе мы познакомились с основными синтаксическими понятиями, используемыми для формирования правильных конструкций языка Perl. Узнали из каких основных элементов состоит Peri-программа, а также разработали и выполнили нашу первую программу.
Ассоциативные массивы
Ассоциативные массивы
Ассоциативные массивы,
называемые также хеш-массивами или просто хешами, — это то, чем гордятся программисты на языке Perl. Они позволяют легко создавать динамические структуры данных — списки и деревья разных видов, с помощью которых можно реализовать функциональность простой системы управления базой данных. Подобной конструкции не найти ни в одном современном языке программирования.
Ассоциативные массивы отличаются от массивов скаляров тем, что в них для ссылки на элементы используются строки, а не числовые индексы, т. е. концептуально они представляют собой список не просто значений элементов массива, а последовательность ассоциированных пар ключ/значение. Ключ является строковым литералом, и именно он и используется для доступа к ассоциированному с ним значению массива.
В программе хеши задаются аналогично массивам скаляров с помощью конструктора, представляющего собой список, заключенный в круглые скобки, в котором пары ключ/значение следуют друг за другом:
(ключ_1, значение_1, ключ_2, значение_2, ... , ключ_п, значение_п)
Для хранения ассоциативных массивов, как и для других элементов данных, используются переменные, первым символом которых является символ процента "%". Переменные, в которых хранятся ассоциативные массивы, часто называют
хеш-переменными.
Ассоциативный массив создается во время операции присвоения такой переменной списка значений:
%т = ("Имя", "Ларри", "Фамилия", "Уолл");
Замечание
Для краткости мы будем иногда говорить, что при создании массива его переменной присваивается список, подразумевая при этом, что в правой части операции присваивания задан конструктор массива.
В ассоциативном массиве %т ключами являются строки "имя" и "Фамилия", а ассоциированными с ними значениями, соответственно, "ларри" и "УОЛЛ". Теперь, чтобы получить значение, соответствующее какому-либо ключу, следует воспользоваться конструкцией:
$т{"ключ"}
Обратите внимание, что при работе с
элементом
ассоциативного массива, символ хеш-переменной "%" заменяется на символ скалярной переменной "$". Аналогично мы поступали и при ссылке на элемент массива скаляров. Единственное отличие — ключ задается в фигурных скобках. Итак, чтобы, например, присвоить некоторой скалярной переменной значение элемента хеш-массива %т, следует воспользоваться следующим оператором:
$surname = $m{"Фамилия"};
Теперь скалярная переменная $ surname имеет в качестве своего значения строку
Уолл.
Замечание
Интерпретация списка как последовательности пар ключ/значение происходит
только
при операции его присвоения хеш-переменной. Если список, присваивается переменной массива скаляров, то он интерпретируется как простая последовательность значений элементов массива.
Инициализация хеш-массива с помощью списка, элементы которого отделяются друг от друга символом запятая ",", не очень удобно, так как в длинном списке трудно выделять соответствующие пары ключ/значение. Для улучшения наглядности пару ключ/значение можно соединить последовательностью символов "=>", заменив ей разделяющую запятую в списке. По правде говоря, и запятая ",", и символы "=>" представляют собой знаки операций в Perl, причем операция "=>" эквивалентна операции "запятая" с той лишь разницей, что ее левый операнд всегда интерпретируется как строковый литерал, даже если он не заключен в кавычки.
Замечание
Интерпретация левого операнда операции "=>" как строкового литерала справедлива для последовательности символов, в которой используются буквы латинского алфавита. Буквы русского алфавита вызовут ошибку интерпретации, так как последовательность символов не будет распознана как слово языка Perl.
Рассмотренный нами ранее хеш-массив %т можно инициировать и таким способом:
%т = (
"Имя" => "Ларри",
"Фамилия" => "Уолл" );
Если бы мы хотели в качестве индексов имени и фамилии использовать английские слова name и surname, то этот же ассоциированный массив можно было бы задать следующим оператором:
%т = (
Name => "Ларри",
Surname => "Уолл" );
Добавить новый элемент ассоциативного массива или изменить значение существующего очень легко. Достаточно присвоить его элементу, определяемому заданным ключом, значение в операторе присваивания:
$т{"Имя"} = "Гарри"; # Изменили значение существующего элемента. $т{"Телефон"} = "345-56-78"; # Добавили новый элемент.
Если при использовании подобной конструкции ассоциативный массив еще не существовал, то при выполнении операции присваивания сначала будет создан сам массив, а потом присвоится значение его элементу. Ассоциативные массивы, как и массивы скаляров, являются динамическими: все добавляемые элементы автоматически увеличивают их^размерность.
Удалить элемент ассоциативного массива можно только с помощью встроенной функции delete:
delete($m{"Телефон"}); # Удалили элемент с ключом "Телефон".
Совет
При изменении значения элемента ассоциативного массива с помощью ключа следует проверять правильность его задания (отсутствие дополнительных пробелов, регистр букв), так как в случае несоответствия заданного ключа элемента с существующими в хеш-массив просто добавится новый элемент с заданным ключом
При работе с ассоциативным массивом часто требуется организовать перебор по множеству всех его ключей или значений. В языке существуют две встроенные функции — keys и values, которые представляют в виде списка, соответственно, ключи и значения ассоциативного массива, Следующий фрагмент программы
print keys(%m), "\n"; # Печать ключей. print values(%m), "\n";
# Печать значений.
отобразит на экране монитора строку ключей и строку значений массива %т
Фамилия Имя Телефон УоллЛарри345-56-11
Обратите внимание, что они отображаются не в том порядке, как задавались с помощью конструктора массива.
Замечание
При создании элементов ассоциативного массива они сохраняются в памяти в порядке, удобном для их .последующего извлечения. Поэтому при его печати последовательность элементов не соответствует порядку их задания. Для упорядочивания значений хеш-массивов следует воспользоваться встроенной функцией сортировки.
Итак, хеш-массивы позволяют обращаться к своим элементам не с помощью числового индекса, а с помощью индекса, представленного строкой. Но что же здесь такого замечательного, какие возможности предоставляет подобная конструкция? Огромные. И первое, что приходит на ум, — это использовать ключи как аналог ключей реляционных таблиц. Правда, хеш-массивы не позволяют непосредственно хранить запись, а только один элемент, но и этого уже достаточно, чтобы создать достаточно сложные структуры данных (пример 3.7).
#! peri -w %friend = (
"0001", "Александр Иванов",
"0002", "Александр Петров",
"0003", "Александр Сидоров" ); %city = (
"0001", "Санкт-Петербург",
"0002", "Рязань", '"0003", "Кострома" ) ; %job = ( •
"0001", "учитель",
"0002", "программист",
"0003", "управляющий"
) ; . ' $person = "0001";
print "Мой знакомый $friend{$person}\n"; print "живет в городе $city{$person}\n"; print "и имеет профессию $job{$person}.\п";
В этом примере создана простейшая база данных знакомых, в которой хранятся их имена, места жительства и профессии. Перечисленная информация содержится в разных хеш-массивах с одинаковым набором ключей, которые и связывают информацию по каждому человеку. Выполнение программы примера 3.6 приведет к отображению на экране монитора следующего текста:
Мой знакомый Александр Иванов живет в городе Санкт Петербург и имеет профессию учитель.
Если изменить значение переменной $ person на другой ключ, то отобразится связанная информация о другом человеке.
Это только простейший пример, который может навести читателя на более плодотворные идеи применения хеш-массивов, одну из которых нам хотелось бы сейчас наметить: создание связанного списка.
Связанный список —
это простейшая динамическая структура данных, расположенных в определенном порядке. Каждый элемент связанного списка состоит из некоторого значения, ассоциированного с данным элементом, и ссылки на следующий элемент списка. Последний элемент списка не имеет ссылки на следующий, что обычно реализуется в виде пустой ссылки. Для окончательного задания связанного списка следует объявить переменную, указывающую на первый элемент списка, которую называют заголовком. Для связанного списка определяются операции выбора, удаления и добавления элемента списка относительно заданного. Графически связанный список можно представить так, как показано на Рисунок 3.1, где указатель на следующий элемент обозначен серым цветом.
Рис 3.1. Графическое представление связанного списка
С помощью хеш-массивов связанный список реализуется просто. Для этого следует значение элемента задать в качестве ключа для следующего за ним элемента списка, определив таким образом указатель на следующий элемент. Значением последнего элемента в хеш-массиве (с ключом, равным значению последнего элемента связанного списка) будет пустая строка "". Переменная-заголовок должна иметь значение, равное ключу первого элемента списка. В примере 3.8 показана реализация связанного списка, а также добавление нового элемента.
%linked_list = ( "начало" => "первый", "первый" => "третий",
"третий" => ""
);
$header = "начало";
# Добавление элемента со значением "второй"
# после элемента со значением "первый".
$temp = $linked_lis.t{ "первый"};
# Запомнили старый, указатель ., $linked_list{"второй"} = $temp;
# Добавили новый элемент. $linked_list{"первый"} = "второй";
# Указатель на новый элемент.
$item = $header;
# Печать нового связанного.списка.
while ($linked_list{$item}) { # Пока не дойдем до пустой строки ""
print $linked_list{$item}, "\n"; # будем печатать значения элементов.
$item = $linked_list{$item};
}
Результатом выполнения программы примера 3.8 будет печать значений элементов нового связанного списка в следующем порядке:
первый второй третий
Этот пример только демонстрационный, чтобы показать легкость реализации подобной динамической структуры. При действительной реализации связанного списка следует все возможные действия оформить в виде функций, которые в дальнейшем использовать для работы со связанным списком.
Массивы скаляров
Массивы скаляров
Массив, в отличие от скалярного типа данных, представляющего единственное значение, — это тип данных, предназначенный для хранения и обработки нескольких скалярных данных, ссылаться на которые можно с помощью индекса. Все массивы Perl одномерны и их можно мыслить как некий линейный список скаляров. Для извлечения какого-либо значения, хранимого в массиве, достаточно одного индекса. В программе массив задается с помощью специальной синтаксической конструкции языка Perl, называемой конструктором массива. Он представляет собой список скалярных значений, заключенный в круглые скобки. Элементы списка отделяются друг от друга запятыми и представляют элементы массива:
(скаляр_1, скаляр_2, ... , скаляр_n)
Как и в других языках программирования, массив Perl представляет набор однотипных данных — скаляров, но скалярные данные могут быть как числовыми, так и строковыми, поэтому, с точки зрения других языков программирования, в массивах Perl хранятся смешанные данные — числа и строки. В качестве скаляра в конструкторе массива может использоваться числовой или строковый литерал или скалярная переменная, чье значение и будет являться элементом массива:
(5, "умножить на", 4) # Используются только литералы.
($first, 'равно', $second) # Используются значения скалярных переменных.
Массив может состоять из неограниченного числа элементов, а может не иметь ни одного. В таком случае его называют пустым массивом. Конструктор пустого массива — круглые скобки без содержащегося в них списка ().
Как отмечалось в начале параграфа, массив характеризуется тем, что к любому его элементу можно обратиться при помощи индекса (целого литерала или скалярной переменной, принимающей целое значение), который задается в квадратных скобках непосредственно после конструктора массива. Индексы массивов в языке Perl начинаются с о, а отрицательные индексы позволяют обратиться к элементам в обратном их заданию порядке:
(5, "умножить на", 4)[0] # Первый элемент массива: 5.
(5, "умножить на", 4)[2] # Последний элемент массива: 4.
(5, "умножить на", 4)[-1] f Последний элемент массива: 4.
(5, "умножить на", 4) [-3] # Первый элемент массива: 5.
При использовании отрицательных индексов индекс -1 соответствует последнему элементу массива, а индекс -п, где п — количество элементов массива, первому элементу массива.
Нельзя использовать индекс для извлечения элементов списка, возвращаемого некоторыми функциями Perl. Индекс можно применять только к массивам или их конструкторам, а для этого достаточно заключить список в круглые скобки. Например, конструкция stat(@m) [0] вызовет ошибку компиляции, если возвращаемым значением функции stat является список, тогда как конструкция (stat (@m)) [0] приведет к желаемому эффекту.
В качестве элемента списка в конструкторе массива можно использовать конструктор массива, но мы не получим в этом случае, как ожидает читатель, знакомый с другими языками программирования, массив массивов, т. е. многомерный массив, к элементам которого можно обратиться с помощью нескольких индексов. Дело в том, что если в качестве элемента списка в конструкторе массива используется конструктор массива, то его элементы добавляются к элементам массива, определяемого внешним конструктором, т. е. каждый его элемент становится элементом внешнего массива, увеличивая количество его элементов на количество элементов внутреннего массива. Например, массив
(1, (2, 3), 4}
эквивалентен массиву
(1, 2, 3, 4)
(О реализации многомерных массивов при помощи ссылок см. .)
В программе массивы хранятся в специальных переменных, имена которых начинаются с символа "@". Объявление переменной массива, или просто массива, чаще всего осуществляется в операторе присваивания (=), в правой части которого обычно задается конструктор массива:
Sarray = ($ml, '+', $m2, '=', $ml+$m2);
В этом примере также продемонстрировано, что в конструкторе массива можно использовать выражения, о которых речь пойдет в следующей главе.
Задать или получить значения элементов массива, хранящегося в переменной, можно и с помощью индекса. Однако "операцию" индексирования нельзя применять непосредственно к имени переменной массива, ее следует применять к переменной, в которой первый символ заменен на символ скалярной переменной $. Подобное "неудобство" связано с последовательным проведением в Perl использования первого символа переменной для указания ее типа: ведь элемент массива является ничем иным как скаляром. Примеры использования индекса с переменными массива представлены ниже:
$ml[0] = "Первый"; # Задает первый элемент массива @ml.
$ml[l] = "Второй"; # Задает второй элемент массива @ml.
@m2 = (1, 2, 3, 4, 5); # Задание массива @т2.
print @ml, "\n", $m2[0]; # Печать массива toil и
# первого элемента массива @m2.
Массивы в Perl являются динамическими. Добавление нового элемента в массив автоматически увеличивает его размер. С помощью индекса можно добавить элемент, отстоящий от последнего определенного элемента массива на любое число элементов, и это действие не приведет к ошибке. Просто все промежуточные элементы будут не определены (их значение будет равно пустой строке ""). При такой реализации массивов программисту не надо заботиться, как в других языках программирования, что индекс выйдет за размеры массива. В случае обращения к не существующему элементу массива Perl возвращает значение, равное нулевой строке.
Замечание
Если для интерпретатора peri включен режим отображения предупреждающих сообщений во время выполнения, то в случае обращения к не определенному или не существующему элементу массива будет отображаться сообщение, что значение элемента не определено.
В любой момент можно определить число элементов массива. Для этого следует воспользоваться синтаксической конструкцией
$ #имя_массива
которая возвращает максимальный индекс массива или -1, если массив не определен. Чтобы получить количество элементов массива, следует к возвращаемому значению этой конструкции добавить 1, так как индексы массивов в Perl начинаются со.
При работе с массивами самой "утомительной" процедурой может оказаться процедура присваивания значений элементам массива. Хорошо, если все необходимые данные для заполнения массива большой размерности уже существуют в каком-либо текстовом файле. Тогда можно присвоить значения элементам массива, воспользовавшись операцией чтения из внешнего файла, но об этом в следующей главе. А если необходимо в программе создать массив натуральных чисел до 1000 включительно? Неужели надо писать 1000 операторов присваивания, или организовывать цикл, в котором осуществлять соответствующие присваивания, или создавать текстовый файл, содержащий эти числа? К счастью, нет! В Perl для подобных ситуаций предусмотрена операция диапазон, которую можно использовать в конструкторе массива. Например, чтобы создать массив натуральных чисел до 1000, достаточно одного оператора:
@naturalNumbers = (1..1000);
Общий синтаксис операции диапазон следующий:
первое_число..последнее_число
Она определяет набор чисел (целых или вещественных), начинающихся с первое_число и не превосходящих последнее_число, в котором последующее больше предыдущего на единицу. Эта операция действительна не только для чисел, но и для алфавитно-цифровых строк, но в этом случае увеличение на единицу означает увеличение на единицу ASCII-кода символа строки. Ниже показаны примеры использования операции диапазон со строковыми данными:
"а".."с" i Соответствует: "а", "b", "с"
"BCY".."BDB" # Соответствует: "BCY", "BCZ", "BDA", "ВОВ"
Эта операция особенно удобна для задания целых чисел, начинающихся с нуля, например, для представления дня месяца в дате вида "01.02.2000":
@ day_ofjnonth = ("01".."31");
(Подробнее операция диапазон рассматривается .)
Переменные массива и элемент массива можно подставлять в строки, ограниченные двойными кавычками. Если интерпретатор встречает в строке переменную массива, то он вставляет в нее значения элементов массива, отделенные друг от друга пробелами. Результатом выполнения следующих операторов
@т = (1. .3);
print "Числа@{т}являются целыми";
будет строка
Числа! 2 являются целыми
Все правила определения идентификатора скалярной переменной при ее подстановке в строку переносятся и на переменную массива.
В связи с возможностью подстановки переменной массива можно указать быстрое решение проблемы распечатки содержимого массива. Дело в том, что если в операторе печати просто указать переменную массива, то его элементы выводятся сплошной строкой без пробелов между ними, что является неудобным при работе с числовыми данными. Если вставить переменную массива в строку, то при ее печати между элементами массива будут вставлены пробелы. Следующие два оператора печати
print @m, "\n"; print "@m\n";
отобразят массив @m предыдущего примера по-разному:
123
123
Подстановка в строку элемента массива с помощью индексного выражения ничем не отличается от подстановки скалярной переменной: элемент массива, полученный с помощью индекса, является скалярной величиной. Однако здесь может возникнуть одна интересная проблема, если в программе определена скалярная переменная с таким же идентификатором, что и у массива. Значение этой переменной или значение соответствующего элемента массива будет вставлено в строку? Например, если в программе определена скалярная переменная $var и массив @var (о том, почему это возможно, мы расскажем в разделе 3.5 данной главы), то значение переменной или элемента массива будет вставлено в строку "$var [0]".
Правильный ответ — значение элемента, так как при синтаксическом анализе интерпретатор будет рассматривать встретившуюся последовательность символов как лексему $var[0], а не как имя переменной $var с последующим символом "[". Если необходимо использовать значение скалярной переменной $var в строке, то можно предложить три способа решения этой проблемы:
"${var}[0.]" # Фигурные скобки ограничивают символы, рассматриваемые
# интерпретатором как единое целое с символом $. "$var\[0]"
# Обратная дробная черта ограничивает идентификатор
# переменной. "$var" .• "[0]" # Конкатенация строк (операция ".") позволяет однозначно
# интерпретировать переменную в первой строке.
Иногда для работы необходимо выделить некоторое подмножество элементов массива, которое мы будем называть фрагментом массива. Можно ее выполнить просто, но не эффективно: присвоить элементам некоторого нового массива значения соответствующих элементов старого, можно воспользоваться специальной конструкцией Perl для выделения фрагмента массива. Если после имени переменной массива в квадратных скобках задать список индексов некоторых элементов массива, то такая конструкция и будет определять фрагмент массива, причем индексы не обязательно должны идти в каком-то определенном порядке — их можно задавать произвольно. Для выделения фрагмента, состоящего из последовательно идущих элементов массива, можно использовать знакомую нам операцию диапазон. Фрагмент массива сам является массивом, и поэтому его можно использовать в правой части оператора присваивания. Несколько примеров создания фрагментов массива приведено ниже:
@т = (10..19); # Исходный массив:
# (10, 11, 12, 13, 14, 15, 16, 17, 18, 19). @т[0, 2, 4, 6, 8];
# Фрагмент 1: (10, 12, 14, 16, 18). @т[6, 4, 5, 8, 6];
# Фрагмент 2: (16, 14, 15, 18, 16). @т[2..4];
# Фрагмент 3: (12, 13, 14). @т[8, 2..4, 0];
# Фрагмент 4: (18, 12, 13, 14, 10).
При выделении фрагмента массива используется имя переменной массива, начинающейся с символа "@", тогда как при ссылке на элемент массива префикс имени переменной заменяется на символ "$". Здесь опять прослеживается последовательное использование префикса для задания типа переменной. Фрагмент массива является массивом, а потому следует использовать символ "@".
Переменные
Переменные
Итак, мы познакомились с тремя основными типами данных и соответственно тремя типами переменных языка Perl, используемых для их обработки и хранения в программе. В этом последнем параграфе данной главы мы суммируем наши знания о переменных и дополним некоторыми аспектами, связанными с их реализацией в интерпретаторе и использованием в простейшей операции присваивания.
Напомним, что переменную можно рассматривать как именованную область памяти, которая не только предоставляет возможность обращаться к содержимому этой области памяти по имени переменной, но и задает тип хранимых в ней данных, определяя, таким образом, формат хранения данных и набор применимых к ним операций.
Первый символ имени переменной языка Perl определяет ее тип. В языке Perl можно определить переменные трех типов: скаляр, массив и хеш-массив. Для каждого типа переменных интерпретатор создает собственное пространство имен, храня в нем идентификаторы объявленных в программе переменных заданного типа. Это позволяет создавать переменные разных типов с одинаковыми идентификаторами. В программе Perl бесконфликтно могут сосуществовать и скалярная переменная $var, и массив скаляров @var, и хеш-массив %var.
Так как имя переменной всегда начинается с одного из символов "$", "@" или "%", то использование в качестве идентификатора "предопределенных" слов не приводит к конфликтам в процессе интерпретации программы.
Следует отметить, что в языке Perl существует достаточно большой набор специальных встроенных переменных, которые можно использовать для получения или изменения разнообразной системной информации. Большинство из них являются скалярными переменными с "идентификатором", состоящим из одного специального символа, например, $~, $__ и т. д.
(Подробно специальные переменные рассматриваются в .)
Немного забегая вперед, скажем, что переменные используются в выражениях, которые, в свою очередь, могут являться операндами определенных в языке операций. Интерпретация операций и значений их операндов в Perl зависит от контекста, в котором они вычисляются. Существует два основных контекста: скалярный и списковый. Например, если левым операндом операции присваивания является скалярная переменная, то правый операнд вычисляется в скалярном контексте, т. е. его значением должен быть скаляр; если левый операнд массив или хеш (или фрагмент массива или хеша), то правый операнд вычисляется в списковом контексте, т. е. должен предоставить значение, являющееся списком.
Замечание
Использование конструктора массива с элементами, являющимися скалярными переменными, в качестве левого операнда операции присваивания предписывает вычислять правый операнд в списковом контексте.
Переменные массивов и хешей, а также их конструкторы, используемые в качестве операндов операции присваивания (=), будут иметь разные значения в зависимости от используемого контекста.
В списковом контексте конструктор массива будет иметь значение, представляющее собой все значения списка в заданном порядке, тогда как в скалярном контексте он будет иметь значение, равное значению последнего элемента списка:
@аггау = (0, 2, 4); # Массив скаляров баггау
# содержит три элемента: 0, 2, 4. $last = (0, 2, 4);
# Значение скалярной переменной $last равно 4.
Переменная массива скаляров в списковом контексте возвращает список всех элементов массива, а употребленная в скалярном контексте, в отличие от своего конструктора, будет иметь значение, равное числу элементов массива:
@new_array = Sarray; # Массив @new_array
# содержит все элементы массива @аггау. $number = Sarray;
# Скалярная переменная $number
# равна 3 - числу элементов массива Sarray.
В качестве левого операнда операции присваивания можно использовать заключенный в круглые скобки список, элементами которого являются скалярные переменные, а правым операндом может быть конструктор массива или переменная массива. В этом случае каждой переменной левого списка присваивается значение соответствующего элемента правого списка. Если количество элементов правого списка меньше числа скалярных переменных левого списка, то переменные, которым не хватило значений, будут не определены. Несколько примеров приводится ниже:
($а, $b = (1, 2, 3); # $а = 1, $ b = 2.
($а, $b $с) = (1, 2); tt $а= 1, $ b =2, $с = "".
В языке Perl каждая операция имеет вычисляемое значение. Значением операции присваивания со скаляром в качестве правого операнда является значение этого скаляра в любом контексте. Если правым операндом является конструктор массива или массив, то в списковом контексте значением операции присваивания будет список элементов массива, а в скалярном контексте — число элементов массива.
$х = ( @а = (1, 2) ); # $х = 2.
Хеш-переменные так же, как и массивы скаляров, ведут себя по-разному в разных контекстах. Употребленные в списковом контексте, они вычисляются в виде обычного списка, состоящего из элементов всех пар ключ/значение. Однако порядок элементов в списке может не соответствовать порядку задания пар в хеше. Это связано с тем, что внутренняя реализация хеш-массивов основана на использовании хеш-таблиц для ускорения поиска соответствия ключ/значение, поэтому при вычислении хеша в виде списка порядок следования "пар" ключ/значение и не соответствует порядку их задания, но после ключа всегда следует его значение.
%hash = ( I => I, 2 =>2, 3 => 3, 4=> 4 );
@list = %hash; # @list = (blue, 3, green, 2, red, 1, black, 4)
В скалярном контексте вычисляемым значением хеш-массива является строка, состоящая из числа использованных участков записей (bucket) и числа выделенных участков записей, разделенных символом "/". Если присвоить скалярной переменной хеш-массив %hash предыдущего примера, то ее значением будет строка "4/8", которая говорит, что 4 из 8 выделенных участков записей хеш-таблицы используется.
Любая переменная в Perl может находиться в состоянии: определена или не определена. Если ей присвоено какое-либо значение, то такая переменная считается определенной. Если ей не было присвоено значение до ее использования в каком-либо выражении, то такая переменная считается не определенной. Чтобы узнать, определена или нет переменная, можно воспользоваться встроенной функцией defined, которая возвращает г (Истина) в случае определенности переменной и пустую строку "", когда переменная не определена:
@т = (1,2);
defined @т; '# Возвращает 1, массив скаляров @т определен.
defined $т; # Возвращает "", переменная $т не определена.
Определенную переменную можно в любой момент сделать неопределенной, выполнив функцию undef с параметром, равным имени этой переменной:
@т = (1,2);
defined @т; # Возвращает 1, массив скаляров @т определен.
undef @m;
defined @m; tt Возвращает "", массив скаляров @т не определен.
Если переменная сделана не определенной с помощью функции undef, TO она, естественно, теряет присвоенное ранее ей значение.
И последнее, о чем нам здесь хотелось бы упомянуть в связи с обсуждением переменных Perl, — это области видимости переменных, т. е. области доступности переменных. Во всех приведенных примерах все переменные являются глобальными, они доступны из любой точки программы. Язык Perl, однако, позволяет создавать переменные с областью видимости, ограниченной блоком или телом подпрограммы. Это так называемые локальные, или синтаксические переменные, имена которых могут совпадать с именами глобальных переменных, и которые существуют, только пока выполняются операторы некоторого блока или подпрограммы. После завершения выполнения операторов блока, эти переменные уничтожаются.
(Более подробно локальные переменные описаны .)
Скалярный тип данных
Скалярный тип данных
Скалярный тип данных в Perl предназначен для представления и обработки числовых данных (чисел) и последовательности символов, называемых строками. Для задания в программе перечисленных данных используются буквальные константы, или литералы: числовые и строковые.
Числовые литералы
используются для представления обычных чисел, необходимых для реализации какого-либо алгоритма в программе Perl. Обычно используются числа с основанием десять, или десятичные числа, но язык позволяет использовать и восьмеричные (с основанием восемь), и шестнадцатеричные (с основанием шестнадцать) числа, которые полезны при работе с содержимым памяти компьютера в процессе решения некоторых системных задач.
Десятичные числа могут быть целыми или вещественными с дробной частью, которые в программировании часто называют числами с плавающей точкой из-за способа их представления и хранения в памяти компьютера. Соответствующие им литералы ничем не отличаются от записи подобных чисел в математике: последовательность цифр без пробелов для целых чисел и последовательность цифр, в которой точка отделяет целую часть от дробной, для вещественных чисел (пример 3.1).
Пример 3.1. Числовые литералы
123 # Целое десятичное число.
234.89 # Вещественное число.
0.6780 # Вещественное с нулевой целой частью
678 # Незначащие нули можно не задавать
1_000_000.67 # Для отделения разрядов в целой части числа
# можно использовать символ подчеркивания.
Для вещественных чисел с плавающей точкой можно использовать и экспоненциальную форму записи:
[цифры] .
[цифры]
[Е | е] [+1 -] [цифры]
Эта форма записи означает, что для получения значения числа следует его мантиссу, заданную в форме действительного числа с точкой ([цифры]. [цифры]), умножить на десять в степени числа со знаком, заданного в его экспоненциальной части после символа Е или е (пример 3.2).
Пример 3.2. Экспоненциальная форма записи вещественных чисел
10.67Е56 # Знак "+" в экспоненте можно опускать.
10.67е+06 # Так экспонента лучше читаема.
1е-203 # Число близко к машинному нулю.
1е+308 # Число близко к бесконечно большому числу.
Замечание
Интерпретатор языка Perl представляет все числа (и целые, и вещественные) в формате чисел с плавающей точкой удвоенной точности. Это означает, что реально нельзя задать больше шестнадцати значащих цифр мантиссы, а экспонента ограничена диапазоном от -323 до +308. Интерпретатор не сгенерирует ошибки, если мантисса будет превосходить 16 цифр, а экспонента 3 цифры, но при отображении таких чисел мантисса будет приведена к шестнадцати значащим цифрам. Если экспонента меньше нижнего предела, то будет выводиться нуль, а если больше верхнего предела, то используется специальный символ 1.#INF, обозначающий бесконечно большое число. Подобный алгоритм представления очень больших и очень маленьких чисел не приводит к возникновению, соответственно, ошибок переполнения и исчезновения порядка, свойственной многим языкам программирования. Если задать целое число с числом значащих цифр больше 15, то при выводе оно будет отображаться как вещественное в экспоненциальной форме.
Некоторые системные установки или анализ некоторых системных параметров легче осуществлять с использованием чисел, представленных в восьмеричной или шестнадцатеричной системах счисления. Форма записи таких чисел аналогична их синтаксису в языке С: любое целое число, начинающееся с нуля "о", трактуется интерпретатором как восьмеричное целое число, а символы, непосредственно следующие за комбинацией "Ох", рассматриваются как шестнадцатеричные цифры. При использовании восьмеричных чисел следует помнить, что в них не может быть цифры больше, чем 7, а в шестнадцатеричных числах кроме десяти цифр от о до 9 используются буквы А или а, в или ь, с или с, о или d, Е или е, F или f для обозначения недостающих цифр числа (пример 3.3).
Пример 3.3. Восьмиричные и шестнадцатиричные числа
010 # Восьмеричное 10, равное десятичному 8.
0x10 # Шестнадцатеричное 10, равное десятичному 16.
0239 # Вызовет ошибку интерпретации: нельзя использовать цифру 9.
OxAIFf # Соответствует 41477 десятичному.
OxGA # Вызовет ошибку интерпретации: нельзя использовать букву G.
Замечание
Задание шестнадцатеричных цифр — это единственный случай в Perl, когда прописные и строчные буквы идентичны. В других случаях их употребления, например в идентификаторах, они различны.
Внимание
Нельзя вместо последовательности символов "Ох", идентифицирующей шестнадцатеричные числа, использовать последовательность "ох".
Строковые литералы,
или просто
строки,
представляют последовательность символов, заключенную в одинарные ('), двойные (") или обратные (') ка
вычки, которая рассматривается как единое целое. Использование одинарных и двойных кавычек для задания строк аналогично их применению для этих же целей в системе UNIX.
В строке, ограниченной одинарными кавычками, нельзя использовать ESC-, или управляющие последовательности, а также в нее нельзя подставить значение переменной. Единственное исключение составляют две управляющие последовательности: (V) и (\\). Первая используется для отображения одинарной кавычки в самой строке, так как иначе интерпретатор рассматривал бы первую, встретившуюся ему одинарную кавычку как признак завершения строки, что не соответствовало бы ее включению в строку. Вторая последовательность используется для отображения самой обратной косой черты. Примеры задания строковых литералов, ограниченных одинарными кавычками, можно найти в табл. 3.2.
Таблица 3.2.
Символьные литералы, ограниченные одинарными кавычками
Строка |
Отображение |
Комментарий |
'Простая строка #1' |
Простая строка #1 |
Строка без управляющих последовательностей |
'Vperl.exeV ' |
'perl.exe' |
Строка с одинарными кавычками |
'D: \\perl.exe' |
D: \perl . ехе |
Строка с обратной дробной чертой |
'Последовательность \n' |
Последовательность \n |
Управляющая последовательность \n не влияет на отображение строки |
'Завтрак Бутерброд с ветчиной Чашка кофе ' |
Завтрак Бутерброд с ветчиной Чашка кофе |
Многострочный символьный литерал отображается в нескольких строках |
Замечание
Esc-последовательности, состоящие из обратной, косой черты (\), за которой следует буква или комбинация цифр. В них символ обратной косой черты рассматривается как символ, изменяющий значение буквы. Они вместе являются одним целым и выполняют определенное действие при выводе на устройство отображения, например, переход на новую строку (\п). Комбинация цифр трактуется как ASCII-код отображаемого символа. Название таких последовательностей происходит от английского слова "escape", означающего изменять смысл. Их еще называют
управляющие последовательности.
Строковый литерал может распространяться на несколько строк программы (см. последний литерал табл. 3.2). Для этого при его вводе с клавиатуры следует использовать клавишу <Enter> для перехода на новую строку.
Многострочные литералы отображаются на стольких строках, на скольких они заданы. Это означает, что символ перехода на новую строку, введенный с клавиатуры, сохраняется в символьном литерале, ограниченном одинарными кавычками. Следует заметить, что это справедливо и для строковых литералов, ограниченных двойными кавычками.
Строки в двойных кавычках позволяют вставлять и интерпретировать управляющие последовательности, а также осуществлять подстановку значений переменных, содержащих скаляры или списки. Управляющие последовательности (табл. 3.3) при выводе строк могут интерпретироваться как символы новой строки, табуляции и т. п., а могут изменять регистр следующих за ними букв.
Таблица 3.3.
Управляющие последовательности
Управляющая последовательность |
Значение |
\a |
Звонок |
\b |
Возврат на шаг |
\e |
Символ ESC |
\f |
Перевод формата |
\n |
Переход на новую строку |
\r |
Возврат каретки |
\t |
Горизонтальная табуляция |
\v |
Вертикальная табуляция |
\$ |
Знак доллара |
\@ |
Амперсанд или AT коммерческое |
\0nnn |
Восьмеричный код символа |
\xnn |
Шестнадцатеричный код символа |
\cn |
Эмулирует нажатие комбинации клавиш + . Например, \сС соответствует + |
\l |
Переводит следующий символ в нижний регистр |
\u |
Переводит следующий символ в верхний регистр |
\L |
Переводит следующую за ней последовательность символов, ограниченную управляющей последовательностью \Е , в нижний регистр |
\Q |
В следующей за ней последовательности символов, ограниченной управляющей последовательностью \Е , перед каждым не алфавитно-цифровым символом вставляет обратную дробную черту |
\U |
Переводит следующую за ней последовательность символов, ограниченную управляющей последовательностью \Е , в верхний регистр |
\E |
Ограничивает действие управляющих последовательностей \L, \Q И \U |
\\ |
Символ обратной дробной черты |
\" |
Двойные кавычки |
\' |
Одинарные кавычки |
Замечание
Если после обратной косой черты в строковом литерале, ограниченном двойными кавычками, следует символ, который не составляет с ней управляющую последовательность, то обратная косая черта не отображается при выводе строки на устройство отображения.
Строковые литералы в двойных кавычках удобно использовать для структурированного вывода текстовой информации, заданной одной строкой. Примеры строк в двойных кавычках представлены в табл. 3.4.
Таблица 3.4.
Символьные литералы, ограниченные двойными кавычками
Строка |
Отображение |
Комментарий |
"'\Uline\E #1" |
LINE #1 |
Управляющие последовательности перевода регистра \l, \u, \L и \и действуют только на буквы латинского алфавита и не применимы к буквам русского алфавита |
"Конец страницы\f" |
Конец страницы |
При выводе на экран монитора или в файл в конце строки отображается символ перехода на новую страницу; при выводе на принтер печать начинается с новой страницы после вывода этой строки |
" \t3aвтpaк\nБyтepброд с ветчиной\пЧашка кофе\n" |
Завтрак Бутерброд с ветчиной Чашка кофе |
Символьный литерал задан одной строкой с управляющими символами |
Том в устройстве D не имеет метки Серийный номер тома: 1F66-19F2 Содержимое каталога D:\PerlOurBook
<КАТАЛОГ> 09.01.00 16:01 .
<КАТАЛОГ> 09.01.00 16:01 ..
EXAMPLE PL 32 23.01.00 11:56 exarrple.pl
01 <КАТАЛОГ> 11.01.00 14:12 01
02 <КАТАЛОГ> 11.01.00 14:12 02
03 <КАТАЛОГ> 11.01.00 14:12 03
PERLINF ТХТ 1 781 12.01.00 11:39 perlinf.txt
EXAMPLE1 PL 347 18.01.00 18:02 examplel.pl
3 файл(а,ов) 2 160 байт
5 каталог(а,ов) 78 086 144 байт свободно
В Perl, как и в UNIX, строки в обратных кавычках используются для "ввода" в программу результатов выполнения не только системных команд, но и выводимых на экран монитора результатов выполнения другой программы, так как всегда можно передать на выполнение командной оболочке имя загружаемого модуля программы.
Замечание
Некоторые символы (их еще называют метасимволы) имеют специальное значение для командной оболочки. К ним относятся *,- <, >, ?, | и &. В системе UNIX, чтобы изменить интерпретацию метасимвола как символа со специальным значением, ставят перед ним обратную косую черту, которая изменяет (escape) его специальное назначение. Теперь он рассматривается командной оболочкой просто как символ, представляющий самого себя. Если во вводимой строке много таких специальных символов, то пользователю необходимо перед каждым поставить обратную косую черту, что приводит к плохой читаемости всей строки. Во избежание подобных "затруднений" в UNIX используются строки в одинарных кавычках, в которых все символы интерпретируются так, как они есть. Подобную же функцию одинарные кавычки выполняют и в языке Perl.
При работе в UNIX широко используется подстановка значений переменных в строку команды, которая передается на обработку оболочке shell. При задании команды в строке ввода используются строки в двойных кавычках, которые так же, как и строки в одинарных кавычках, отменяют специальные значения метасимволов, за исключением символа $, который используется для подстановки значения переменной. Обратная косая черта перед ним изменяет его специальное значение. Именно этот механизм строк в двойных кавычках послужил прототипом для аналогичных конструкций в языке Perl.
Строка в обратных кавычках используется в UNIX для подстановки стандартного вывода команды, т. е. содержимое строки в обратных кавычках интерпретируется командной оболочкой как команда системы, которая должна быть выполнена, а результат ее выполнения подставлен вместо строки в обратных кавычках. В Perl эта конструкция перенесена без всяких изменений.
Все обрабатываемые программой данные хранятся в некоторой области памяти компьютера, определяемой своим адресом. Для удобства программирования доступа к данным языки высокого уровня, и Perl здесь не исключение, используют
переменные,
с помощью которых программист может ссылаться на данные, расположенные в памяти, или изменять их содержание. Переменная задается своим именем, которое используется программой для обращения к области памяти и извлечения хранящихся в ней данных или, наоборот, записи данных в область памяти. Обычно говорят, что в переменных хранятся данные, хотя, как мы видим, это не совсем так. Правильнее говорить, что переменная определяет именованную область памяти, в которой хранятся некоторые данные.
Более того, переменная определяет тип данных, хранимых в области памяти, на которую она ссылается. В большинстве языков программирования переменные до их использования в программе объявляются как переменные определенного типа, информируя транслятор, что в них можно хранить данные соответствующего типа. Как мы помним, в Perl нет операторов объявления переменных определенного типа; они автоматически объявляются при первом их использовании в конструкциях языка, например в операторе присваивания значения переменной. Любая переменная определяется заданием своего имени, которое представляет собой правильный идентификатор языка. В Perl имя любой переменной состоит из специального символа (префикса), определяющего тип переменной, за которым следует идентификатор. Для переменных скалярного типа (пример 3.5), или просто скалярных переменных, таким определяющим символом является знак доллара "$".
# Правильные имена скалярных переменных. $Name; $name_surname; $name_l;
# Неправильные имена скалярных переменных.
$l_name; # Идентификатор не может начинаться с цифры.
$Name@Surname; # Недопустимый символ @
Скалярная переменная
может хранить только одно скалярное данное: числовое или строковое, причем не существует никакого способа определить, каг кой именно тип скалярных данных в ней содержится. Дело в том, что при использовании этих переменных в различных операциях хранимые в них данные
автоматически
преобразуются из одного типа в другой, т. е. в арифметических операциях строка преобразуется в число, а в строковых операциях числовое значение преобразуется в строковое. Преобразование строки в числовое значение осуществляется, если она содержит последовательность символов, которая интерпретируется как число, иначе интерпретатор генерирует ошибку. Шестнадцатеричные числа с префиксом "ох" и десятичные числа с символом подчеркивания для отделения триад в целой части числа, заданные как строки, не преобразуются в числа, а последовательность цифр,' начинающаяся с о, не интерпретируется как восьмеричное число. Для преобразования строк, содержащих представления шестнадцатеричных и восьмеричных чисел, в числовые значения следует пользоваться функцией oct. Как отмечалось выше, строки в двойных кавычках не только интерпретируют управляющие символы, но и позволяют подставлять значения скалярных переменных. Это означает, что в строке можно задать имя переменной, ко-торое при вычислениях заменяется значением, содержащимся в переменной на момент вычислений. (Подобную процедуру подстановки значения переменной в символьную строку в дальнейшем для краткости мы будем называть просто
подстановкой переменной.)
Например, следующая последовательность операторов
$s = "\$10";
$п = "Книга стоит $s долларов.";
print $n;
отобразит на экране монитора строку
Книга стоит $10 долларов.
Замечание
Можно подставлять значения не только скалярных переменных, но и массивов скаляров, элементов массивов и хешей, а также сечений массивов и хешей. Об этом мы расскажем в следующих параграфах этой главы, определяя соответствующие типы данных и их переменные.
При подстановке переменной ее имя должно быть отделено разделителями от остальных символов строки, причем это правило не обязательно для первого символа имени переменной, так как интерпретатор, встретив символ "$" в строке, ограниченной двойными кавычками, начинает выделять правильный идентификатор.
Разделителями могут быть пробелы или управляющие последовательности. Можно и явно указать идентификатор переменной, задав его в фигурных скобках. Подобная техника демонстрируется следующим фрагментом программы:
$day = 'пятницу';
$number = 5;
$html = "HTML";
$s = "${html}-документ отослан B\n$day\t$number февраля.";
print $s;
Результатом выполнения этого фрагмента будет отображение двух строк на экране монитора:
HTML-документ отослан в пятницу 5 февраля.
Переменная $htmi подставляется с явным указанием ее идентификатора, для выделения идентификаторов остальных переменных используются разделители.
Подставлять можно скалярные переменные, значения которых определены с помощью числовых и любых типов строковых литералов, причем строка в обратных кавычках интерпретируется как команда операционной системы.
Замечание
Рассмотренные в этом параграфе различные типы кавычек для задания строковых литералов на самом деле являются всего лишь удобной формой записи операций языка Perl: q//, qq/7, qx/7.
(Эти операции подробно будут рассмотрены в главе 4).
Синтаксический анализатор языка Perl при анализе текста программы выделяет слова (не заключенная в кавычки последовательность алфавитно-цифровых символов) и определяет их принадлежность набору ключевых слов. Если слово не является ключевым, то интерпретатор рассматривает его как строку символов, заключенную в кавычки. Это позволяет задавать строковые литералы, не заключая их в кавычки:
$day = Friday; # Тождественно оператору $day = 'Friday';
Такие слова без кавычек в тексте программы иногда еще называют
простыми словами
(barewords).
Задание строковых литералов без кавычек возможно
только
для литералов, содержащих буквы латинского алфавита. Попытка применить подобную технику для литералов, содержащих буквы русского алфавита, приведет к ошибке компиляции.
Завершая разговор о литералах, следует упомянуть о специальных литералах языка Perl: _LINE_, _FILE_, _END_ и _DATA_. Они являются самостоятельными лексемами, а не переменными, поэтому их нельзя вставлять в строки. Литерал _LINE_ представляет номер текущей строки текста программы, а _FILE_— имя файла программы. Литерал _END_ используется для указания логического конца программы. Информация, расположенная в файле программы после этого литерала, не обрабатывается интерпретатором, но может быть прочитана через файл с дескриптором DATA. Последний литерал _DATA_ аналогичен литералу _END_, только дополнительно он открывает файл с дескриптором DATA для чтения информации, находящейся в файле программы после него. Программа примера 3.6 демонстрирует использование специальных литералов.
#! /per!520/bin/perl -w
$file = _FILE_;
$prog = _LINE_;
print "Находимся в строке: $prog\n",
"Файл: $file";; _END_ print "Текст после лексемы _END_";
Результат работы этой программы будет следующим, если файл программы хранится в файле D:\PerlEx\examplel.exe:
Находимся в строке: 3
Файл: D:\PERLEX\EXAMPLE1.PL
Вывода последнего в программе оператора печати не наблюдается, так как он расположен после лексемы _END_.
в следующем фрагменте кода Perl:
Упражнения
Найдите ошибки в следующем фрагменте кода Perl:
$m.= 'Исходные данные:\п'; @data = ( 1, 2, 3, 4} ; print $m, 'Запись: Sdata';
Что напечатают следующие операторы и почему:
$т = "Скаляр \$m\n";
@т = ( 1, 2, 3);
print "Значение равно $m[0]\n";
print "Значение равно $m [0]";
Предположим, что есть группа слушателей курса по языку Perl, состоящая из 10 человек. В середине курса слушатели сдают промежуточный экзамен, а в конце — выпускную работу. За экзамен и за выпускную работу выставляется оценка по пятибалльной системе. По окончании курса каждый слушатель получает удостоверение, в котором указано, естественно, его имя, а также оценки за экзамен и выпускную работу. Разработайте базу данных слушателей курса, которую можно использовать для автоматизации подготовки удостоверений об успешном окончании курса. (Указание: воспользуйтесь хеш-массивами.)
Дополните программу примера 3.8 удалением первого и последнего элемента связанного списка. (Указание: воспользуйтесь функцией delete ().)
После выполнения упражнения 4 в связанном списке останется один элемент. Удалите его, распечатайте, а затем снова добавьте два элемента в список и распечатайте.
Перечислите три встроенных типа данных
Вопросы для самоконтроля
Перечислите три встроенных типа данных языка Perl.
В чем отличие числового литерала от строкового.
Объясните различие между строкой, ограниченной одинарными кавычками, и строкой, ограниченной двойными кавычками.
Каким образом можно выполнить системную команду из программы Perl?
Что такое массив скаляров и ассоциативный массив?
Как задаются в программе массивы и хеш-массивы?
Как объявляются в программе переменные для хранения скалярных данных, массивов скаляров и хеш-массивов?
Что такое интерполяция переменной?
Можно ли интерполировать массивы скаляров и хеш-массивы?
Какие два контекста для операции присваивания вы знаете, и как ведут себя массивы скаляров и хеш-массивы в них?
Арифметические операции
Арифметические операции
Язык программирования, предоставляя возможность определения разнообразных типов данных, должен обеспечивать их обработку, т. к. его основной целью является реализация алгоритмов обработки данных. Выполнение допустимых действий над данными осуществляется с помощью набора определенных в языке программирования операций. Операция — это выполнение определенного действия над операндами, результатом которого является новое значение. С точки зрения математики операцию можно рассматривать как некую функцию, которая по заданным переменным (операндам) вычисляет новое значение. Все знают со школьной скамьи четыре основных арифметических действия, выполняемых над числами: сложение (+), вычитание (-), умножение (*) и деление (/). Эти действия (операции) мы всегда выполняем над двумя числами (операндами), получая в результате новое число. Язык программирования определяет не только арифметические операции над числовыми данными, но и операции, применимые к другим допустимым типам данных. Это могут быть операции над строками, массивами и т. д. Важно только одно, что есть операция, определяемая своим знаком, и есть участвующие в ней операнды, в совокупности позволяющие получить (вычислить) новое значение, которое может принадлежать к одному из допустимых типов. В качестве операндов можно использовать литералы, переменные и выражения, представляющие собой комбинации основных операций. Общий синтаксис операции можно представить следующим образом:
операнд знак_операции операнд
Для некоторых операций один из операндов может быть опущен. В этом случае операция называется одноместной или унарной, например, вычисление противоположного по знаку значения операнда -$т. Если в операции участвуют два операнда, то операция называется двуместной или бинарной. Практически все допустимые операции являются унарными или бинарными, но в некоторых современных языках программирования определена од-на-единственная условная тернарная операция, требующая три операнда. Значением этой операции является второй или третий операнд, в зависимости от истинности и ложности первого:
операнд_1 ? операнд_2 : операнд_3
Синтаксически выражение представляется в виде последовательности операндов и знаков операций, которая обычно интерпретируется слева направо учетом порядка старшинства операций, т. е. правил, определяющих, в какой последовательности выполняются присутствующие в выражении операции. Для изменения порядка выполнения используются круглые скобки. Результатом обработки интерпретатором выражения является некоторое вычисленное значение, которое используется в других конструкциях языка, реализующих алгоритм обработки данных.
В этой главе подробно рассматриваются операции языка Perl и их использование при конструировании выражений. В языке Perl определено несколько десятков операций. Их можно сгруппировать по типу выполняемых действий (арифметические, побитовые, логические, отношения, присваивания), на какие данные они воздействуют (строковые, списковые, файловые), и не относящиеся ни к одному из перечисленных типов (операции запятая, ссылки, разыменования и выбора). Следуя именно такой классификации операций языка Perl, мы и познакомим с ними нашего читателя. Одни операции будут разобраны подробно, о других мы только дадим общее представление, отнеся более детальное описание в другие главы нашей книги.
Все арифметические операции можно разбить на три группы: бинарные, унарные и увеличения/уменьшения. Их основное назначение — выполнить определенные вычисления над числовыми данными, но во всех арифметических операциях в качестве операндов могут выступать и строковые данные, причем не обязательно, чтобы они конвертировались в числовые данные.
Бинарные арифметические операции
Бинарные арифметические операции — это известные всем четыре арифметических действия: сложение (+), вычитание (-), умножение (*) и деление (/), к которым добавляются еще два: остаток от деления двух целых чисел (%) и возведение в степень (**). Примененные к числовым данным или строковым, которые содержат .правильные литералы десятичных чисел, они выполняют соответствующие арифметические действия (пример 4.1).
3.14 + 123; # Результат: 126.14
"3.14" + "123"; # Результат: 126.14
"3.14" + 123; # Результат: 126.14
"3.14" * 10; # Результат: 31.4
300 - 200; # Результат: 100
300 / 200; # Результат: 1.5
3 % 2; # Результат: 1
2 ** 3; . # Результат: 8
(-2) ** 3; # Результат: -8 '
2 ** (-3); # Результат: 0.125
2.5 ** 1.5; # Результат: -23.95284707521047
Как видим, бинарные арифметические операции "работают" именно так, как мы привыкли их использовать в обычных арифметических вычислениях в нашей повседневной жизни.
Замечание
Если операнд в операции получения остатка от деления целых чисел (%) является вещественным числом с дробной частью, то он преобразуется к целому простым отбрасыванием дробной части, после чего операция выполняется над целыми числами.
Замечание
Нельзя возводить отрицательное число не в целую степень. Если такое случается, то интерпретатор не выдает никакой ошибки, но результатом такой операции является нуль: (-2.5) ** (1.3) = о.
В качестве операндов бинарных арифметических операций можно использовать строки, не содержащие правильные числовые литералы. В этом случае интерпретатор попытается выделить, начиная с первого символа, из содержимого строки число и использовать его в качестве соответствующего операнда заданной операции. Если не удается выделить правильный числовой литерал, то операнд принимает значение, равное о. Подобные ситуации демонстрируются в примере 4.2.
"3fl4" •+ "12-30"; # Результат: 15 ("3" + "12")
"а!20" + "12-30"; # Результат: 12 ("О" + "12")
."а!20" + "-0012-30"; # Результат: -12 ("О" + "-12")
Замечание
Если установить режим отображения предупреждающих сообщений интерпретатора (ключ -w), то при попытке использовать в бинарных арифметических операциях строки, не содержащей правильные числовые литералы, будет отображено сообщение вида:
Argument "al20" isn't numeric in add at D:\EXAMPLE1.PL line 2.
Бинарные арифметические операции выполняются в скалярном контексте. Это означает, что операндами должны быть скалярные переменные, а переменные массивов скаляров и хеш-массивов принимают значения, равные, соответственно, количеству элементов массивов скаляров или количеству использованных в хеш-таблице записей в соответствии с требованиями скалярного контекста (пример 4.3).
@m = (2, 4, 6, 8, 10) ;
%ml = ( 1 => "а", 2 => "Ь"};
$n = 100;
$n + @m; # Результат: 105 (100 + 5)
@m + %ml; # Результат: 7 (5+2)
Замечание
В скалярном контексте хеш-массив принимает строковое значение, состоящее из числа использованных участков записей в хеш-таблице и числа выделенных участков записей, разделенных символом "/" (см. главу 3). Используемое в арифметических операциях число получается выделением из этой строки числового литерала, который как раз и соответствует количеству использованных в хеш-таблице записей.
Унарные арифметические операции
В языке Perl есть только две унарные арифметические операции (+) и (-). Унарный плюс +, примененный к данным любого типа, представленным литералами или своими переменными, не имеет никакого семантического эффекта. Он полезен перед выражением в круглых скобках, стоящим непосредственно после имени функции, если необходимо чисто визуально акцентировать тот факт, что функция фактически является списковой операцией.
Унарный минус (-) выполняет арифметическое отрицание числового операнда. Это означает, что если число было отрицательным, то оно станет положительным, и наоборот. Если операндом является идентификатор, то результатом выполнения этой операции будет строка, состоящая из символа "-", за которым следует идентификатор. Если операндом является строка, начинающаяся с символа минус или плюс, то результатом также будет строка, в которой минус заменен на плюс и наоборот. Для строк, не начинающихся с плюса или минуса, операция унарного минуса добавляет его первым символом в строку. Все перечисленные случаи употребления унарного минуса показаны в примере 4.4.
-'12.09'; # Результат: -12.09
-(-12.09);•# Результат: 12.09
-id; # Результат: '-id'
-'+id"; # Результат: '-id'
-"-id"; # Результат: "+id"
-'а!20'; # Результат: '-а!20'
Операции увеличения и уменьшения
Операции увеличения (++) и уменьшения (--) аналогичны таким же операциям в языке С. ( Авторы языка Perl не скрывают, что они многое заимствовали из этого языка.) Результат этих операций зависит от того, стоят ли они перед (префиксная форма) или после переменной (постфиксная форма). При использовании префиксной формы они, соответственно, увеличивают или уменьшают числовое значение переменной на единицу до возвращения значения. Постфиксная форма этих операций изменяет числовое значение переменной после возвращения ими значения. Действие этих операций на числовые переменные иллюстрируется примером 4.5 (операторы фрагмента программы выполняются последовательно).
$n = 10.7; # Начальное значение
$infl = —$n; # Результат: $infl = 9.7и$n=9.7
$inf2 = ++$n; # Результат: $inf2 = 10.7 и $n = 10.7
$postl = $n--; # Результат: $postl = 10.7 но $n= 9.7
$post2 = $n++; # Результат: $post2 = 9.7 но $n= 10.7
Операция увеличения (префиксная и постфиксная), примененная к переменной, содержащей строку определенного вида, выполняется несколько необычно. Если строка состоит только из латинских букв, то возвращаемым значением операции увеличении будет строка, в которой последняя буква заменена на следующую по порядку букву алфавита, причем строчная заменяется строчной, а прописная прописной. Если строка завершается идущими подряд буквами "z" или "z", то все они заменяются соответственно на "а" или "А", а стоящая перед ними в строке буква заменяется на следующую букву алфавита. Если вся строка состоит из букв "z" и "z", то кроме замены этих букв в соответствии с предыдущим правилом, перед ними добавляется строчная или прописная буква "а" в зависимости от того, строчная или прописная буква "z" стояла первой в строке.
Аналогичные действия осуществляются, если строка завершается последовательностью цифр: последняя цифра увеличивается на единицу. Если строка завершается идущими подряд цифрами 9, то все они заменяются на о, а примыкающий к ним символ "увеличивается" на единицу: для буквы он переходит в следующий по алфавиту, а для цифры в следующую по порядку цифру. Если последовательность целиком состоит из девяток, то все они заменяются на нули, перед которыми добавляется единица. Префиксная и постфиксная формы операции действуют как обычно. Несколько иллюстраций этих операций представлены в примере 4.6.
$s = "abc"
$sl = ++$s; # Результат: $sl = "abd"
$s = "abC";
$sl = ++$s; # Результат: $sl = "abD"
$s = "abz";
$sl = ++$s; # Результат: $sl = "аса"
$s = "abzZz";
$sl = ++$s; # Результат: $sl = "acaAa"
$s = "ab09";
$sl = ++$s; # Результат: $sl = "ablO"
$s = "99"; .
$sl = ++$s; # Результат: $sl = "100"
Замечание
Операция уменьшения (—) работает со специальными строками так же, как и с обычными. Осуществляется попытка выделить числовой литерал, начиная с первого символа. Если такое оказывается возможным, то числовое значение строки приравнивается выделенному числовому литералу, если нет — ее значение считается равным 0. После этого применяется операция уменьшения к вычисленному числовому значению строки
Именованные унарные операции
Именованные унарные операции
В языке Perl определено большое количество встроенных функций, выполняющих разнообразные действия. Некоторые из них, с точки зрения синтаксического анализатора языка, на самом деле являются унарными операциями, которые и называют именованными унарными операциями, чтобы отличить их от унарных операций со специальными символами в качестве знаков операций (например, унарный минус "-", операция ссылки "\", логического отрицания "!" и т. д.). Некоторые из именованных унарных операций перечислены ниже:
chdir, cos, defined, goto, log, rand, rmdir, sin, sqrt, do, eval, return (Является ли функция унарной операцией, можно определить в приложении 1.)
К именованным унарным операциям относятся также все операции проверки файлов, синтаксис которых имеет вид:
-символ [имя_файла\дескриптор_файла]
Например, для проверки существования файла определена операция -е, выяснить возможность записи в файл можно операцией -w.
(Более подробно операции проверки файлов рассматриваются в части 7 .)
Логические операции
Логические операции
Рассмотренные в предыдущем параграфе операции сравнения используются в условном операторе if (о нем и других операторах Perl в следующей главе) для организации ветвления в программе. Однако, иногда желательно проверять одновременно результаты нескольких операций сравнения и предпринимать соответствующие алгоритму действия. Можно подобную ситуацию запрограммировать с помощью вложенных операторов if, а можно в одном операторе использовать сложное выражение, результатом вычисления которого будет, например, истинность двух или более каких-либо операций сравнения. Для формирования подобных проверок и служат логические операции языка Perl.
В языке определены бинарные операции логического сравнения | 1 (ИЛИ), s & (И) и унарная операция логического отрицания !. Их действие аналогично действию соответствующих математических операций исчисления предикатов. Результатом операции | | (логическое ИЛИ) является Истина, если истинен хотя бы один из операндов, в остальных случаях она возвращает Ложь (остальные случаи представляют единственный вариант, когда оба операнда ложны). Операция логического И && возвращает в качестве результата Истину, только если оба операнда истинны, в противном случае ее результат Ложь. Операция логического отрицания ! работает как переключатель: если ее операнд истинен, то она возвращает Ложь, если операнд имеет значение Ложь, то ее результатом будет Истина.
Замечание
В языке Perl нет специальных литералов для булевых значений Истина и Ложь. В качестве значения Истина принимается любое скалярное значение, не равное нулевой строке "" или числу 0 (а также его строковому эквиваленту "О"). Естественно, нулевая "" строка и о (вместе с его строковым эквивалентом "О") представляют значение Ложь.
Начиная с Perl 5.001, в язык были введены логические операции or, and, not и хог. Первые три полностью аналогичны логическим операциям | |, && и !, тогда как операция хог реализует исключающее ИЛИ:
Истина хоr Истина = Ложь
Истина хоr Ложь = Истина
Ложь хоr Истина = Истина
Ложь хоr Ложь = Ложь
Единственное отличие этих логических операций от рассмотренных ранее заключается в том, что они имеют наименьший приоритет при вычислении сложных выражений.
В Perl вычисление логических операций ИЛИ и И осуществляется по "укороченной схеме". Это непосредственно связано со смыслом этих операций. Если при вычислении операции ИЛИ определено, что значение ее первого операнда Истина, то при любом значении второго операнда результатом всей операции будет Истина, поэтому нет смысла вообще вычислять второй операнд. Аналогично для операции логического И: если значение первого операнда Ложь, то результат всей операции Ложь вне зависимости от значения второго операнда. В отличие от операций отношения, результатом которых может быть о (или пустая строка "") или 1, соответствующие булевым значениям Ложь и Истина, результатом логических операций является значение последнего вычисленного операнда. Пример 4.10 иллюстрирует вычисление логических операций.
$opl = 0; $ор2 = "s"; $орЗ = ""; $ор4 = 25; $ор5 = "0";
$ор4 II $ор2; # Результат: истина. Значение: 25.
$ор2 I| $ор4; # Результат: истина. Значение: "s".
$opl && $ор2; # Результат: ложь. Значение: 0.
$ор2 && $ор4; # Результат: истина. Значение: 25.
!$ор2; # Результат: ложь. Значение: "".
not $орЗ; # Результат: истина. Значение: 25.
$ор4 and $op5; # Результат: ложь. Значение: "".
Свойство логических операций языка Perl вычисляться по "укороченной схеме" можно использовать для управления некоторыми исключительными ситуациями, возникающими в программе в процессе вычислений. Например, можно достаточно элегантно избежать деления на нуль с помощью операции логического ИЛИ:
($х = 0) II ($т = 1/$х);
При вычислении результата этой операции сначала вычисляется левый операнд, который сравнивает значение переменной $х с нулем. Если это значение действительно равно нулю, то результатом операции сравнения будет Истина, а поэтому второй операнд операции логического ИЛИ не вычисляется, так его значение не влияет на результат выполнения логической oпeрации, и не возникает ситуации деления на нуль. Если значение переменной $х не равно нулю, то результатом вычисления первого операнда операции | | будет Ложь, и обязательно будет вычисляться ее второй операнд, в котором осуществляется деление на не равную нулю переменную $х.
Операции конкатенации и повторения
Операции конкатенации и повторения
Бинарная операция конкатенации, или соединения объединяет два строковых операнда в одну строку. Знаком этой операции служит точка ".":
"one_string"."two_string"; # Результат: "one_stringtwo_string"
В новой строке содержимое первого операнда и содержимое второго операнда соединяются без пробела между ними. Обычно эта операция используется для присваивания переменной некоторого нового значения. Если необходимо соединить две или более строки со вставкой пробелов между ними, то следует воспользоваться операцией join (см. гл. 10 "Работа со строками"). Можно, однако, для соединения строк со вставкой пробела (или любого другого символа между ними) воспользоваться свойством подстановки значения скалярной переменной в строку, ограниченную двойными кавычками: $sl = "one_string"; $s2 = "two_string"; $s = "$sl $s2"; # Значение $s: "one_string two_string"
Можно использовать операцию конкатенации строк последовательно в одном выражении для соединения нескольких строк:
$sl = "one";
$s2 = "two";
$s3 = "three";
$s = $sl.$s2.$s3; # Значение $s: "onetwothree"
Операцию конкатенации можно применять и к числовым литералам, и к числовым данным, хранящимся в скалярных переменных. Результатом будет строка, содержащая символьные представления двух чисел:
$nl = 23.5;
$n2 = Зе01;
$n = $nl.$n2; t Значение $n: "23.530"
$n = 23.5.3е01; # Значение $n:'"23.530"
Заметим, что последний оператор выглядит несколько экзотично и его семантика не определяется с первого взгляда.
Для работы со строками в языке Perl предусмотрена еще одна операция — повторение строки х (просто символ строчной буквы "х"). Эта бинарная операция создает новую строку, в которой строка, заданная левым операндом, повторяется определяемое правым операндом количество раз:
"аА" х 2; # Результат: "аАаА"
10.0 х "3"; # Результат: "101010"
101е-1 х 3; # Результат: "101010" $n = 010;
$n х 2; # Результат: "88"
10.1 х 3.9; # Результат: "10.110.110.1"
"101е-1" х 2; # Результат: "101е-1101е-1"
Обратим внимание, что в качестве левого операнда можно использовать и числовые литералы, и переменные, содержащие числовые данные. Правым операндом, задающим число повторений, может быть любое число или строка, содержащая правильное десятичное число.
Эта операция удобна, если надо напечатать или отобразить на экране монитора повторяющийся символ или последовательность символов. Например, следующий оператор выведет на экран монитора строку, целиком состоящую из символов подчеркивания: print "_" х 80;
Левым операндом этой операции может быть список, заключенный в круглые скобки. В этом случае операция повторения х работает как повторитель списка, т. е. ее результатом будет список, в котором список левого операнда повторяется заданное правым операндом количество раз:
(1) х 3; # Результат: (1, 1, 1) (1, 2) х 2; # Результат: (1, 2, 1, 2)
Это пример использования операции Perl в разных контекстах: скалярном и списковом (о контекстах мы поговорим ниже в этой же главе). Операция повторения в списковом контексте удобна для задания массива скаляров с одинаковыми значениями элементов или групп элементов:
@аггау = ("а", "b") х 2; # Результат: Оаrrау = ("а", "Ь", "а", "Ь") @аrrау = ("а") х 3; # Результат: @аrrау = ("а", "а", "а")
Аналогично, эту операцию можно использовать для инициализации хеш-массива одинаковыми значениями:
@keys = ( one, two, three); # Определение ключей хеш-массива. @hash{@keys} = ("а") х @keys; # Инициализация значений хеш-массива.
В последнем операторе присваивания в правой части массив скаляров @keys используется в списковом контексте и представляет список своих значений, тогда как в левой части он используется в скалярном контексте и имеет значение, равное числу своих элементов.
Знак операции повторения х 'следует отделять пробелами от операндов, так как иначе он может быть воспринят интерпретатором, как относящийся к лексеме, а не представляющий операцию повторения. Например, при синтаксическом разборе строки
$nx$m;
интерпретатор определит, что в ней идут подряд две переменные $nх и $m, a не операция повторения содержимого переменной $п, что приведет к синтаксической ошибке.
Операции отношения
Операции отношения
Для сравнения скалярных данных или значений скалярных переменных язык Perl предлагает набор бинарных операций, вычисляющих отношения равенства, больше, больше или равно и т. п. между своими операндами, поэтому эту группу операций еще называют операциями отношения. Для сравнения числовых данных и строковых данных Perl использует разные операции. Все они представлены в табл. 4.1.
Таблица 4.1. Операции отношения
Операция | Числовая | Строковая | Значение |
Равенство | == | eq | Истина, если операнды равны, иначе ложь |
Неравенство | != | ne | Истина, если операнды не равны, иначе ложь |
Меньше | < | lt | Истина, если левый операнд меньше правого, иначе ложь |
Больше | > | gt | Истина, если левый операнд больше правого, иначе ложь |
Меньше или равно | <= | le | Истина, если левый операнд больше правого или равен ему, иначе ложь |
Больше или равно | >= | ge | Истина, если правый операнд больше левого или равен ему, иначе ложь |
Сравнение | cmp | 0, если операнды равны 1, если левый операнд больше правого -1, если правый операнд больше левого |
Результатом операций отношения (кроме последней сравнения) является Истина, значение 1, или Ложь, пустая строка "".
Замечание
Значение истина в арифметических операциях интерпретируется как число 1, а в строковых как строка "1". Значение ложь в арифметических операциях интерпретируется как число 0, а в строковых как пустая строка " ".
Числовые операции отношения
Числовые операции отношения применяются к числовым данным, причем один или оба операнда могут задаваться строкой, содержащей правильное десятичное число. Если в числовых операциях отношения какой-либо из операндов задан строкой, содержимое которой не представляет правильное десятичное число, то его значение принимается равным о и отображается предупреждение о некорректном использовании операнда в числовой операции отношения (если включен режим отображения предупреждений интерпретатора Perl). Смысл операций отношения для числовых данных соответствует обычным математическим операциям сравнения чисел (пример 4.7).
123 > 89; # Результат: 1 (истина)
123 < 89; # Результат: "" (ложь)
123 <= 89; # Результат: "" (ложь)
89 <= 89; # Результат: 1 (истина)
23 >= 89; # Результат: "" (ложь)
23 <=> 89; # Результат: -1 (правый операнд больше левого)
89 <=> 23; # Результат: 1 (правый операнд больше левого)
Применение числовых операций сравнения не представляет сложности, однако при сравнении на равенство десятичных чисел с плавающей точкой могут проявиться эффекты округления, связанные с ограниченным количеством значащих цифр в мантиссе представления действительных чисел в компьютере и приводящие к "неправильной", с точки зрения пользователя работе операций сравнения. Пример 4.8 иллюстрирует подобную ситуацию.
#! peri -w
$z = 0.7;
$zz =• 10+0.7-10; # Переменная $zz содержит число 0.7
# Печать строки "z равно zz", если равны значения переменных $z и $zz print "z равно zz\n" if ($z == $zz);
При попытке выполнить пример 4.8 мы с удивлением обнаружим, что наша программа ничего не напечатает. В чем же дело? Разгадка лежит в операторе вычисления значения переменной $zz. При выполнении арифметических операций в результате ошибок округления получается значение 0.699999999999999 (можете вставить оператор печати переменной $zz и убедиться в этом), хотя и близкое к 0.7, но не равное ему в точности. Следовательно, операция сравнения отработала верно!
Совет
Не используйте операцию сравнения на равенство вещественных чисел, ее результат может не соответствовать ожидаемому с точки зрения математики. Если необходимо проверить равенство двух вещественных чисел, то лучше сравнивать абсолютное значение их разности с некоторым очень маленьким числом (в зависимости от требуемой точности):
abs($a-$b) <= 0.00000001; # Проверка равенства
Строковые операции отношения
Сравнение строковых данных базируется на их упорядочении в соответствии с таблицей кодов ASCII, т. е. символ с меньшим кодом ASCII предшествует символу с большим кодом. Сравнение строк осуществляется посимвольно слева направо. Это означает, что если равны первые символы строк, то сравниваются вторые и если они равны, то сравниваются третьи и т. д. Причем, если строки разной длины, то в конец строки меньшей длины добавляется недостающее для равенства количество символов с кодом о. Следует отметить, что в отличие от некоторых других языков программирования в Perl замыкающие строку пробельные символы являются значимыми при сравнении строк. В примере 4.9 показаны сравнения строк, иллюстрирующие изложенные правила.
"A" It "a"; | # Результат: истина (код "А" - \101, код "а" - \141) |
"a" It "aa"; |
# Результат: истина (к строке "а" добавляется символ # с кодом \000, который меньше кода \141 # второго символа "а" строки правого операнда) |
"a" It "a "; |
# Результат: истина (к строке "а" добавляется символ # с кодом \000, который меньше кода \040 # замыкающего пробела строки правого операнда) |
"12" It "9"; | # Результат: истина (код "1" - \061, код "9" - \071) |
" 9" eq "09"; | # Результат: ложь (код " " - \040, код "О" - \060) |
Операции присваивания
Операции присваивания
Присваивание переменной какого-либо значения, определенного литералом, или присваивание одной переменной значения другой переменной является наиболее часто выполняемым действием в программе, написанной на любом языке программирования. В одних языках это действие определяется с помощью оператора, а в других — с помощью операции. Отличие заключается в том, что в языках, где присваивание является операцией, оно может использоваться в выражениях как его составная часть, так как любая операция вычисляет определенное значение, тогда как оператор всего лишь производит действие. В языке Perl присваивание является операцией, которая возвращает правильное lvalue. Что это такое, мы разъясним буквально в следующих абзацах.
Операция присваивания =, с которой читатель уже немного знаком, является бинарной операцией, правый операнд которой может быть любым правильным выражением, тогда как левый операнд должен определять область памяти, куда операция присваивания помещает вычисленное значение правого операнда. В этом случае и говорят, что левый операнд должен быть правильным lvalue (от английского left value — левое значение). А что мы можем использовать в программе для обозначения области памяти? Правильно, переменные. Следовательно, в качестве левого операнда операции присваивания можно использовать переменную любого типа или элемент любого массива. (В языке Perl существуют и другие объекты, которые можно использовать в качестве левого операнда операции присваивания, но об этом в свое время.) Следующая операция простого присваивания $а = $Ь+3;
вычислит значение правого операнда и присвоит его переменной $а, т.е. сохранит в области памяти, выделенной для переменной $а. Возвращаемым значением этой операции будет адрес области памяти переменной $а (правильное lvalue), или говоря проще, имя скалярной переменной $а, которое снова можно использовать в качестве левого операнда операции присваивания. Таким образом, в языке Perl следующая операция присваивания
($а = $Ь) = 3;
является синтаксически правильной и в результате ее вычисления переменной $а будет присвоено значение з, так результатом вычисления операции присваивания $а = $ь будет присвоение переменной $а значения переменной $ь, а возвращаемым значением можно считать переменную $а, которой в следующей операции присваивается значение з. Читатель спросит: "А зачем городить такие сложности, если тот же самый результат можно получить простой операцией присваивания $а = з?". Действительно, замечание справедливое. Но на этом примере мы показали, как можно использовать операцию присваивания в качестве правильного lvalue. Более интересные примеры мы покажем, когда определим составные операции присваивания, заимствованные из языка С.
Синтаксические правила языка Perl позволяют осуществлять присваивание одного и того же значения нескольким переменным в одном выражении:
$varl = $var2 = $varl[0] =34;
Очень часто при реализации вычислительных алгоритмов приходится осуществлять разнообразные вычисления с использованием значения некоторой переменной и результат присваивать этой же переменной. Например, увеличить на з значение переменной $а и результат присвоить этой же переменной $а. Это действие можно реализовать следующей операцией присваивания:
$а = $а+3;
Однако, язык Perl предлагает более эффективный способ решения подобных проблем, предоставляя в распоряжение программиста бинарную операцию составного присваивания +=, которая прибавляет к значению левого операнда, представляющего правильное lvalue, значение правого операнда и результат присваивает переменной, представленной левым операндом. Таким образом, оператор составного присваивания
$а += 3; I Результат: $а = $а+3
эквивалентен предыдущему оператору простого присваивания. Единственное отличие заключается в том, что его реализация эффективнее реализации простого присваивания, так как в составном операторе присваивания переменная $а вычисляется один раз, тогда как в простом ее приходится вычислять дважды. (Под вычислением переменной понимается вычисление адреса представляемой ею области памяти и извлечение значения, хранящегося в этой области памяти.)
Для всех бинарных операций языка Perl существуют соответствующие составные операции присваивания. Все они, вместе с примерами их использования, собраны в табл. 4.2.
Таблица 4.2. Составные операции присваивания
Операция | Пример | Эквивалент с операцией простого присваивания |
**= | $а **= 3; | $а = $а ** 3; |
+= | $а += 3; | $а = $а + 3; |
-= | $а -= 3; | $а = $а - 3; |
.= | $а .= "а"; | $а = $а . "а"; |
*= | $а *= 3; | $а = $а * 3; |
/= | $а /= 3; | $а = $а / 3; |
%= | $а %= 3; | $а = $а % 3; |
х= | $а х= 3; | $а = $а х 3; |
&= | $а &= $b; | $а = $а & $b; |
|= | $а |= 3; | $а = $а | 3; |
^= | $а ^= 3; | $а = $а ^ 3; |
«= | $а «= 3; | $а = $а « 3; |
»= | $а »= 3; | $а = $а » 3; |
&&= | ||
= | $а = $b == 0; | $а = $а $b == 0; |
$b = 1; | |
$а = ($Ь +=3); | # Результат: $а = $b = 4 |
$а += ($Ь += 3); | # Результат: $а = $а+$b+3 |
( ($а += 2) **= 2) -= 1; | # Результат: $а = ($а+2)**2-1 |
При использовании операции присваивания (простой или составной) в качестве левого операнда другой операции присваивания обязательно ее заключение в круглые скобки. Иначе может сгенерироваться синтаксическая ошибка, или выражение будет интерпретировано не так, как задумывалось. При наличии нескольких операций присваивания в одном выражении без скобок интерпретатор peri начинает его разбор справа. Например, если последнее выражение примера 4.11 записать без скобок
$а += 2 **= 2 -•= 1;
то при его синтаксическом анализе интерпретатор сначала выделит операцию присваивания
2 -= 1;
и сообщит об ошибке, так как ее синтаксис ошибочен (левый операнд не является переменной или элементом массива).
Операции связывания
Операции связывания
Операции сопоставления с образцом, используемые многими утилитами обработки текста в Unix, являются мощным средством и в языке Perl. Эти операции с регулярными выражениями включают поиск (m//), подстановку (s///) и замену символов (tr///) в строке. По умолчанию они работают со строкой, содержащейся в системной переменной $_. Операции =~ и \ ~ связывают выполнение сопоставления с образцом над строкой, содержащейся в переменной, представленной левым операндом этих операций: $_ = "It's very interesting!";
s/very/not/; # Переменная $_ будет содержать строку
# "It's not interesting!"
$m = "my string";
$m =~ s/my/our/; i Переменная $m будет содержать строку
tt "our string"
Возвращаемым значением операции =~ является Истина, если при выполнении соответствующей ей операции сопоставления с образцом в строке была найдена последовательность символов, определяемая регулярным выражением, и Ложь в противном случае. Операция ! ~ является логическим дополнением к операции =~. Следующие два выражения полностью эквивалентны:
$m !~ m/my/our/; not $m =~ m/my/our/;
(Более подробно регулярные выражения и операции связывания рассматриваются .)
Операции ввода/вывода
Операции ввода/вывода
Для взаимодействия и общения с внешним окружением в любом языке программирования предусмотрены операции ввода/вывода. Perl не является исключением. В нем определен ряд операций, обеспечивающих ввод и вывод данных в/из программы.
Операция print
С этой операцией вывода мы уже немного знакомы. Операция print — унарная операция, правым операндом которой служит задаваемый список значений, которые она отображает по умолчанию на экране монитора. Операцию, операндом которой является список, называют списковой операцией. Внешне ее можно задать как вызов функции, заключив ее операнд в круглые скобки. Следующие операции эквивалентны:
print "@m", "\n", $m, "\n"; print("@m", "\n", $m, "\n");
(Более подробно эта операция рассматривается .)
Выполнение системных команд
Операция заключения в обратные кавычки — это специальная операция, которая передает свое содержимое на выполнение операционной системы и возвращает результат в виде строковых данных:
$command = ~dir"; # Переменная $command после выполнения операционной . # системой KOMaHflbi'dir' содержит результат ее # выполнения.
Содержимое строкового литерала в обратных кавычках должно быть, после подстановки значений переменных, которые могут в нем присутствовать, правильной командой операционной системы.
(Более подробно эта операция рассматривается в .)
Операция <>
При открытии файла с помощью функции open () одним из ее параметров является идентификатор, называемый дескриптором файла, с помощью которого можно в программе Perl ссылаться на файл. Операция ввода из файла осуществляется заключением в угловые скобки его дескриптора <дескриптор_файла>. Результатом вычисления этой операции является строка файла или строки файла в зависимости от скалярного или спискового контекста ее использования. Следующие операторы иллюстрируют эту особенность данной операции:
open( MYFILE, "data.dat"); tt Открытие файла "data.dat" и назначение ему
# дескриптора MYFILE
$firstline = <MYFILE>; # Присваивание первой строки файла @remainder = <MYFILE>; # Оставшиеся строки файла присваиваются
# элементам массива скаляров.
Дескриптор файла можно использовать и в операции print, организуя вывод не на стандартное устройство вывода, а в файл, представляемый дескриптором:
print MYFILE @m;
Особый случай представляет операция чтения из файла с пустым дескриптором о: информация считывается либо из стандартного файла ввода, либо из файлов, заданных в командной строке.
(Более подробно операции ввода/вывода из/в файл рассматриваются .) (Работа с файлами более подробно рассматривается .)
Операции заключения в кавычки
Операции заключения в кавычки
Кавычки (одинарные, двойные и обратные) в Perl мы используем для задания строковых литералов, причем получающиеся результирующие строковые данные существенно зависят от используемого типа кавычек: символы строки в одинарных кавычках трактуются так, как они в ней заданы, тогда как некоторые символы ($, @) или даже последовательности символов (\ п , \t) в строке в двойных кавычках выполняют определенные действия. Всё дело в том, что в Perl кавычки — это всего лишь удобный синтаксический эквивалент определенных операций, выполняемых над символами строки. В языке, кроме трех перечисленных операций заключения в кавычки, определен еще ряд операций, выполняющих определенные действия со строковыми данными и внешне похожих на операции заключения в кавычки, на которые мы будем в дальнейшем ссылаться так же, как на операции заключения в кавычки.
Все операции заключения в кавычки представлены в табл. 4.3 с эквивалентным синтаксисом (если таковой существует) и кратким описанием действий, выполняемых при их выполнении.
Таблица 4.3. Операции заключения в кавычки
Общая форма |
Эквивалентная форма |
Значение |
Возможность подстановки | ||||
q{ } |
* i |
Строковый литерал |
Нет | ||||
qq{ } |
it ii' |
Строковый литерал |
Да | ||||
qx{ } |
|
Команда системы |
Да | ||||
qw { } |
0 |
Список слов |
Нет | ||||
m{ } |
// |
Поиск по образцу |
Да | ||||
qr{ } |
|
Образец |
Да | ||||
s { } { } |
|
Подстановка |
Да | ||||
tr{ }{ } |
у/// |
Транслитерация |
Нет | ||||
При использовании общей формы операции заключения в кавычки вместо фигурных скобок {}, представленных в табл. 4.3, можно использовать любую пару символов, выбранную в качестве разделителя. Если выбранный символ не является какой-либо скобкой (круглой, угловой, квадратной или фигурной), то он ставится в начале и в конце строки, к которой должна быть применена соответствующая операция, тогда как в случае использования скобок-разделителей сначала используется открывающая скобка, а в конце закрывающая. Между знаком операции и строками в символах-разделителях может быть произвольное число пробельных символов. Обычно в качестве разделителя программистами Perl используется косая строка "/", хотя это и не обязательно.
В табл. 4.3 в последнем столбце также указывается, осуществляет ли соответствующая операция подстановку значений скалярных переменных и массивов, а также интерпретацию управляющих символов.
В этом параграфе мы остановимся только на первых четырех операциях заключения в кавычки. Остальные операции, как непосредственно связанные с регулярными выражениями, будут подробно рассмотрены в главе 10.
Операция q{}
Эта операция аналогична заданию строкового литерала в одинарных кавычках. В нем каждый символ строки представляет самого себя, подстановка значений переменных не выполняется. Единственное исключение — обратная косая черта, за которой следует символ-разделитель или еще одна обратная косая черта. Эти последовательности символов позволяют ввести непосредственно в строку символ разделителя или обратную косую черту (хотя обратная косая черта и так представляет саму себя). Несколько примеров:
q-сДескриптор \<FILE\»; # Строка символов: Дескриптор <FILE>
д!Каталог \\bin\usr\n!; # Строка символов; Каталог \bin\usr\n
'Каталог \\bin\usr\n'; # Эквивалентно предыдущей операции
Побитовые операции
Побитовые операции
Данные в компьютере представляются в виде последовательности битов. В языке Perl определены бинарные операции побитового логического сравнения целых чисел и строк: & (И), | (ИЛИ) и л (исключающее ИЛИ), а также унарная операция логического отрицания ~. Результат их вычисления зависит от того, к данным какого типа они применяются: числовым или строковым. Эти операторы различают числовые данные и строки, содержимое которых может быть преобразовано в число.
Кроме логических операций побитового сравнения, две операции сдвигают влево («) и вправо (») биты в представлении целых чисел. Эти операторы не работают со строками.
Числовые операнды
Если хотя бы один операнд в бинарных побитовых операциях является числом, то содержимое второго операнда также должно быть числом. Операнд, являющийся строкой символов, преобразуется в числовое значение. В случае несоответствия содержимого строки десятичному числу ее значение принимается равным о и отображается предупреждающее сообщение, если установлен соответствующий режим работы интерполятора. Все числовые операнды преобразуются к целым числам простым отбрасыванием дробной части, никакого округления не происходит.
Чтобы понять сущность побитовых операций над числовыми данными, необходимо представлять, как хранятся в программе целые числа. При задании чисел мы можем использовать одно из трех представлений: десятичное, восьмеричное или шестнадцатеричное. Однако в компьютере числа не хранятся ни в одном из указанных представлений. Они переводятся в двоичные числа — числа с основанием 2, цифры которых и называются битами. Двоичные числа представляют собой запись чисел в позиционной системе счисления, в которой в качестве основания используется число 2. Таким образом, двоичные цифры, или биты, могут принимать значения только о или 1. Например, десятичное число ю, переведенное в двоичное, представляется в виде loio. Для обратного перевода этого числа в десятичную форму представления следует, в соответствии с правилами позиционной системы счисления, произвести следующие действия:
1 * (2**3) + 0 * (2**2) + 1 * -(2**1) + 0 * (2**0)
Язык Perl гарантирует, что все целые числа имеют длину 32 бит, хотя на некоторых машинах они могут представляться и 64 битами. Именно с двоичными представлениями целых чисел и работают все побитовые операции, преобразуя и отрицательные, и положительные числа к целому типу данных.
Операция & побитового логического И сравнивает каждый бит правого операнда с соответствующим битом левого операнда. Если оба сравниваемых бита имеют значение 1, то соответствующий бит результирующего значения операции устанавливается равным 1, в противном случае значением бита результата будет о. В качестве примера рассмотрим следующее выражение
45.93 & 100
Прежде всего, правый операнд преобразуется к целому 45, двоичное представление которого будет
00000000000000000000000000101101
Двоичное представление левого операнда юо будет иметь вид
00000000000000000000000001100100
Результатом побитового логического И будет следующая последовательность битов
00000000000000000000000000100100
Она соответствует десятичному целому числу 36. Следовательно, значением выражения 45.93 & юо будет целое число 36.
Замечание
В этом примере мы специально использовали 32-битное представление целых чисел, хотя для бинарных побитовых операций лидирующие нули не имеют значения. Они существенны только при операциях побитового логического отрицания и сдвигов.
При побитовой операции логического ИЛИ | бит результата устанавливается равным 1, если хотя бы один из сравниваемых битов равен 1. Операция побитового логического ИЛИ для тех же двух чисел
45.93 | 100
даст результат равный юэ, так как при применении побитового логического ИЛИ к операндам
00000000000000000000000000101101 (десятичное 45)
И
00000000000000000000000001100100 (десятичное 100)
дает следующую цепочку битов
00000000000000000000000001101101 (десятичное 109:
2**6+2**5+2**3+2'**2+2**1)
Побитовое исключающее ИЛИ при сравнении битов дает значение \ тогда, когда точно один из операндов имеет значение равное 1. Следовательно, 1 ^ 1=о и о ^ о=о, в остальных случаях результат сравнения битов равен о. Поэтому для тех же чисел результатом операции будет десятичное число 73.
Операция логического отрицания ~ является унарной и ее действие заключается в том, что при последовательном просмотре битов числа все значения о заменяются на 1, и наоборот. Результат этой операции существенно зависит от используемого количества битов для представления целых чисел. Например, на 32-разрядной машине результатом операции ~1 будет последовательность битов
11111111111111111111111111111110
представляющая десятичное число 42949б7294=2 31 +2 30 +...+2 1 , тогда как на 16-разрядной машине эта же операция даст число б534=2 31 +2 30 +...+2 1 .
Бинарные операции побитового сдвига осуществляют сдвиг битов целого числа, заданного левым операндом, влево («) или вправо (») на количество бит, определяемых правым целочисленным операндом. При сдвиге вправо недостающие старшие биты, а при сдвиге влево младшие биты числа дополняются нулями. Биты, выходящие за разрядную сетку, пропадают. Несколько примеров операций сдвига представлено ниже:
# Битовое представление числа 22: (00000000000000000000000000010110)
22 » 2 # Результат: (00000000000000000000000000000101) = 5
22 << 2 # Результат: (00000000000000000000000001011000) = 88
Все перечисленные операции работают и с отрицательными целыми числами, только при их использовании следует учитывать, что они хранятся в дополнительном коде. Двоичная запись неотрицательного целого числа называется прямым кодом. Обратным кодом называется запись, полученная поразрядной инверсией прямого кода. Отрицательные целые числа представляются в памяти компьютера в дополнительном коде, который получается прибавлением единицы к младшему разряду обратного кода. Например, представление числа -1 получается следующим образом:
00000000000000000000000000000001 # положительное число 1 111111111111111111111111111111Ю # обратный код числа 1 11111111111111111111111111111111 # добавляем к младшему разряду 1
# и получаем представление числа -1
Именно с этим кодом числа -i будут работать все побитовые операции, если оно будет задано в качестве операнда одной из них.
Внимание
В языке Perl, как отмечалось в гл. 3, в арифметических операциях используется представление всех чисел в виде чисел с плавающей точкой удвоенной точности. Там же говорилось, что целое число можно задавать с 15 значащими цифрами, т.е. максимальное положительное целое число может быть 999 999 999 999 999. Но это число не имеет ничего общего с представлением целых чисел в компьютере, для которых может отводиться 64, 32 или 16 битов, в зависимости от архитектуры компьютера. Во всех побитовых операциях можно предсказать результат только если операндами являются целые числа из диапазона -2 32 -1. . 2 32 -1, так как ясен алгоритм их представления. Вещественные числа, не попадающие в этот диапазон, преобразуются к целым, но алгоритм их преобразования не описан авторами языка.
Разные операции
Разные операции
В этом параграфе собраны операции, которые не вошли ни в одну из рассмотренных нами групп операций. Две из них упоминались при описании массивов и хешей (операции диапазон и запятая), а третья является единственной тернарной операцией языка Perl (операция выбора).
Операция диапазон
Бинарная операция диапазон ".." по существу представляет две различных операции в зависимости от контекста, в котором она используется.
В списковом контексте, если ее операндами являются числа (числовые литералы, переменные или выражения, возвращающие числовые значения), она возвращает список, состоящий из последовательности увеличивающихся на единицу целых чисел, начинающихся со значения, определяемого левым операндом, и не превосходящих числовое значение, представленное правым операндом. Операцию диапазон часто используют для задания значений элементов массивов и хешей, а также их фрагментов (см. главу 3). Она удобна для организации циклов for и f oreach:
# Напечатает числа от 1 до 5, каждое на новой строке, foreach $cycle (1..5){ print "$cycle\n"; }
# Напечатает строку "12345".
for(l..5){
print; '
}
(Операторы цикла рассматриваются .)
Замечание
Если значение какого-либо операнда не является целым, оно приводится к целому отбрасыванием дробной части числа.
Если левый операнд больше правого операнда, то операция диапазон возвращает пустой список. Подобную ситуацию можно отследить с помощью функции defined О, возвращающей истину, если ее параметр определен, или простой проверкой логической истинности массива, элементам которого присваивались значения с помощью операции диапазон:
$min = 2; .
$max = ~2;
Эаггау = ($min .. $max); # Массив не определен.
print "Эаггау array\n" if defined(@array); # Печати не будет!
print "Sarray array\n" if Эаггау; # Печати не будет!
Замечание
В операции диапазон можно использовать и отрицательные числа. В этом случае возвращается список отрицательных чисел, причем значение левого операнда должно быть меньше значения правого операнда:
(-5..5) # Список чисел: (-2, -1, О, 1, 2). (-5..-10) # Пустой список.
Если операндами операции диапазон являются строки, содержащие буквенно-цифровые символы, то в списковом контексте эта операция возвращает список строк, расположенных между строками операндов с использованием лексикографического порядка:
@а = ("a".."d" ); # Массив @а: "а", "Ь", "с", "d"
@а = ("01".."31" ); # Массив @а: "01", "02", ... , "30", "31"
@а = ("al".."d4" ); # Массив Эа: "al", "a2", "аЗ", "а4"
Если левый операнд меньше правого, с точки зрения лексикографического порядка, то возвращается единственное значение, равное левому операнду.
Замечание
Операция диапазон не работает со строками, содержащими символы национальных алфавитов. В этом случае она всегда возвращает единственное значение, соответствующее левому операнду.
В скалярном контексте операция диапазон возвращает булево значение Истина или Ложь. Она работает как переключатель и эмулирует операцию запятая "," пакетного редактора sed и фильтра awk системы Unix, представляющую диапазон обрабатываемых строк этими программами.
Каждая операция диапазон поддерживает свое собственное булево состояние, которое изменяется в процессе ее повторных вычислений по следующей схеме. Она ложна, пока ложным остается значение ее левого операнда. Как только левый операдц становится истинным, операция диапазон переходит в состояние Истина и находится в нем до того момента, как ее правый операнд не станет истинным, после чего операция снова переходит в состояние Ложь. Правый операнд не вычисляется, пока операция находится в состоянии Ложь; левый операнд не вычисляется, пока операция диапазон находится в состоянии Истина.
В состоянии Ложь возвращаемым значением операции является пустая строка, которая трактуется как булева Ложь. В состоянии Истина при повторном вычислении она возвращает следующее порядковое число, отсчет которого начинается с единицы, т. е. как только операция переходит в состояние Истина, она возвращает 1, при последующем вычислении, если она все еще находится в состоянии Истина, возвращается 2 и т. д. В момент, когда операция переходит в состояние Ложь, к ее последнему возвращаемому порядковому числу добавляется строка "ко", которая не влияет на возвращаемое значение, но может быть использована для идентификации последнего элемента в диапазоне вычислений. Программа примера 4.12 и ее вывод, представленный в примере 4.13, иллюстрируют поведение оператора диапазон в скалярном контексте. Мы настоятельно рекомендуем внимательно с ними ознакомиться, чтобы "почувствовать", как работает эта операция.
#! peri -w
$left =3; # Операнд! $right =2; # Операнд2
# Заголовок таблицы
print "\$i\tflnana3OH\tOnepaHfll\tOnepaHfl2\n";
print '-' х 48, "\n\n";
# Тест операции
for($1=1; $i <= 10; $i++) {
$s = $left..$right;
print "$i\t $s\t\t $left \t\t $right\n";
$slO = 3 if $i==5; # Когда переменная цикла $i равна 5, # $slO устанавливается равной 3.
if ($right==0) {} else {—$right}; # Уменьшение $right на 1, пока
# $ right не достигла значения 0.
—$left; }
Замечание
В целях экономии времени мы не объясняем смысл незнакомых операторов примера 4.12, надеясь, что читатель сможет понять их смысл. Если все же это окажется для него сложным, мы рекомендуем снова вернуться к этой программе после прочтения главы 5.
$1 | Диапазон | Операнд1 | Операнд2 |
1 | 1ЕО | 3 | 2 |
2 | 1ЕО | 2 | 1 |
3 | 1 | 1 | 0 |
4 | 2 | 0 | 0 |
5 | 3 | -1 | 0 |
6 | 4EO | -2 | 2 |
7 | 1EO | -3 | 1 |
8 | 1 | -4 | 0 |
9 | 2 | -5 | 0 |
10 | 3 | -6 | 0 |
На третьем шаге операция становится истинной ($ieft = 1), возвращая i, и правый операнд со значением Ложь ($right = о) не влияет на ее состояние. На следующих шагах 4 и 5 правый операнд остается ложным, а операция возвращает соответственно следующие порядковые числа 2 и з. На шаге 6 операция находится в состоянии Истина и возвращает 4, но правый операнд, принимая значение Истина ($right = о), переводит ее в состояние Ложь, в котором к возвращаемому значению добавляется строка "ЕО" и т. д.
Подобное поведение, связанное с переходом из состояния Истина в состояние Ложь и одновременным изменением возвращаемого значения (добавлением строки "ЕО") эмулирует поведение операции запятая фильтра awk. Для эмуляции этой же операции редактора sed, в которой изменение возвращаемого значения осуществляется при следующем вычислении операции диапазон, следует вместо двух точек в знаке операции ".." использовать три точки "...". Результаты вывода программы примера 4.12, в которой осуществлена подобная замена, представлены в примере 4.14.
$i | Диапазон | Операнд 1 | Операнд2 |
1 | 1 | 3 | 2 |
2 | 2EO | 2 | 1 |
3 | 1 | 1 | 0 |
4 | 2 | 0 | 0 |
5 | 3 | -1 | 0 |
6 | 4EO | -2 | 2 |
7 | 1 | -3 | 1 |
8 | 2 | -4 | 0 |
9 | 3 | -5 | 0 |
10 | 4 | -6 | 0 |
#! peri -w
open{POST, "file.txt") or die "Нельзя открыть файл file.txt!";
LINE:
while(<POST>) {
$temp = 1../^$/; # Истина, пока строка файла не пустая.
next LINE if ($temp); # Переход на чтение новой строки,
# если $temp истинна.
print $_; # Печать первой строки файла после не пустой
last; # Выход из цикла
}
close(POST);
В этой программе для нас интересен оператор присваивания возвращаемого значения операции диапазон переменной $temp. Прежде всего отметим, что эта операция используется в скалярном контексте, причем ее левый операнд представлен числовым литералом. На первом шаге цикла читается первая строка файла и специальной переменной $. присваивается ее номер 1, который сравнивается со значением левого операнда операции диапазон. Результатом этого сравнения является булево значение Истина, и операция диапазон переходит в состояние Истина, в котором она и остается, если первая строка файла не пустая, так как операция поиска по образцу / Л $/ возвращает в этом случае Ложь. Операция next осуществляет переход к следующей итерации цикла, во время которой читается вторая строка файла. Операция диапазон остается в состоянии Истина, если прочитанная строка файла пустая, увеличивая возвращаемое значение на единицу. Далее операция next снова инициирует чтение новой строки файла. Если строка файла пустая, то операция поиска по образцу возвращает значение Истина, переводя тем самым операцию диапазон в состояние Ложь, которое распознается при следующем ее вычислении, поэтому считывается еще одна строка файла (первая после пустой). Теперь операция next не выполняется, так как операция диапазон возвращает Ложь, печатается первая не пустая строка и операция last прекращает дальнейшую обработку файла.
Списковые операции
Списковые операции
Списковая операция — это операция над списком значений, причем список не обязательно заключать в круглые скобки.
Мы уже знакомы с одной из таких операций — операцией вывода на стандартное устройство print. Иногда мы говорили об этой операции как о функции — и это справедливо, так как все списковые операции Perl выглядят как вызовы функций (даже их описание находится в разделе "Функции" документации по Perl), и более того, они позволяют заключать в круглые скобки список их параметров, что еще сближает их с функциями. Таким образом, списковую операцию (или функцию) print можно определить в программе любым из следующих способов:
print $a, "string", $b; # Синтаксис списковой операции, print($a, "string", $b); # Синтаксис вызова функции.
(Встроенные функции более подробно рассматриваются в .)
Замечание
Описание многих встроенных функций (списковых операций) можно найти в главах, в которых вводятся понятия языка, для работы с которыми и предназначены определенные встроенные функции. Например, функции print, printf и write описаны в главе 6.
Ссылки и операция разыменования
Ссылки и операция разыменования
При выполнении программы Perl она, вместе с используемыми ею данными, размещается в оперативной памяти компьютера. Обращение к данным осуществляется с помощью символических имен — переменных, что является одним из преимуществ использования языка высокого уровня типа Perl. Однако иногда необходимо получить непосредственно адрес памяти, где размещены данные, на которые мы ссылаемся в программе с помощью переменной. Для этого в языке определено понятие ссылки, или указателя, который содержит адрес переменной, т. е. адрес области памяти, на которую ссылается переменная. Для получения адреса переменной используется операция ссылка, знак которой "\" ставится перед именем переменной:
$m ='5;
$рт = \$m; f Ссылка на скалярную величину
Ссылки хранятся в скалярных переменных и могут указывать на скалярную величину, на массив, на хеш и на функцию:
@аrrау = (1,2,3);
$раггау = \@аггау; # Ссылка на массив скаляров
%hesh = (опе=>1, two=>2, three=>3);
$phesh = \%hesh; § Ссылка на массив скаляров
Если распечатать в программе переменные-ссылки $pm, $parray и $phesh, то мы увидим строки, подобные следующим:
SCALAR(Ox655a74) ARRAY (Ох655ЫО) HASH(0x653514)
В них идентификатор определяет тип данных, а в скобках указан шестнадцатеричный адрес области памяти, содержащей данные соответствующего типа.
Для получения содержимого области памяти, на которую ссылается переменная-указатель, требуется выполнить операцию разыменования ссылки. Для этого достаточно перед именем такой переменной поставить символ, соответствующий типу данных, на который ссылается переменная ($, @, %):
@keys = keys(%$phash); # Массив ключей хеша @values = values(%$phash); # Массив значений хеша print "$$pm \n@$parray \n@keys \n@values";
Этот фрагмент кода для определенных в нем переменных-ссылок на скаляр, массив и хеш напечатает их значения:
5 # Значение скалярной переменной $m
123 # Значения элементов массива скаляров @аrrау
three two one # Ключи хеша %hash
321 # Значения хеша %hash
Использование описанной выше простой операции разыменования может приводить к сложным, трудно читаемым синтаксическим конструкциям при попытке получить значения элементов сложных конструкций: массива массивов, массива хешей и т. п. Для подобных целей в языке Perl предусмотрена бинарная операция ->, левым операндом которой может быть ссылка на массив скаляров или хеш-массив, а правым операндом индекс элемента массива или хеша, значение которого необходимо получить: print "$parray->[0], .$parray->[1], .$parray->[2]\n"; print "$phash->{one} r $phash->{two}, $phash->{three}\n";
Эти операторы напечатают значения элементов массива ©array и хеша %hash.
(Более подробно ссылки и операции разыменования рассматриваются в .)
Что будет отображено на экране
Упражнения
Что будет отображено на экране монитора при вычислении выражения print print 1;
Определите результат вычисления следующих выражений:
print "О" I I print "1"; print "О" or print "1";
Что будет отображено на экране монитора и каковы будут значения элементов массива @т в результате выполнения следующей операции присваивания: @m = (print "p\n", 2, print 3, 4);
Определите результат выполнения следующих операторов:
$varO = 2; $varl = 1;
$rezl = $varO ** 3 * 2 !I 4 + $varl, $varl++;
$rez2 = ($varl++, $varO ** 3 * 2 4 + $varl, "6");
@rez3 = ($varl++, $varO ** 3 * 2 4 + $varl, "6");
Что напечатает следующий фрагмент программы при вводе числа или строки и почему:
$input = <STDIN>;
$hello = "Hello ";
$hello += $input;
print $hello;
Найдите ошибку в программе: $first_number =34; $second_number = 150; if( $first_number It $second_number ) { print $first_number; }
Выражения
Выражения
С помощью операций в программе можно выполнить определенные действия над данными. Если для реализации алгоритма решения поставленной задачи необходимо произвести несколько действий над определенными данными, то это можно осуществить последовательным выполнением операций, сохраняя при необходимости их результаты во временных переменных, а можно построить одно выражение, составленное из последовательности операций и их операндов, и получить необходимый результат. Итак, выражение можно представить как последовательность операндов, соединенных одной или более операциями, результатом выполнения которой является единственное скалярное значение, массив или хеш. Операнды, в свою очередь, сами могут быть выражениями, что приводит к заданию в программе достаточно сложных выражений, вычисление которых начинается с их синтаксического разбора.
Когда компилятор начинает синтаксический разбор выражения Perl, он прежде всего выделяет в нем элементарные члены, называемые термами, а потом по определенным правилам соединяет их знаками операций, заданными в выражении, т. е. определяет последовательность выполнения операций над термами в соответствии с определенным в языке приоритетом операций. После такого разбора выражение вычисляется в соответствии с полученной последовательностью термов, соединенных знаками операций.
Таким образом, выражение можно мыслить как последовательность термов, соединенных знаками операций.
Именованные блоки
Именованные блоки
В Perl блок операторов, заключенный в фигурные скобки, семантически эквивалентен циклу, выполняющемуся только один раз. В связи с этим обстоятельством можно использовать команду last для выхода из него, а команду redo для повторного вычисления операторов блока. Команда next также осуществляет выход из блока, но отличается от команды last тем, что вычисляются операторы блока continue, который может задаваться для блока операторов в фигурных скобках:
BLOCK1: {
$i = 1;
last BLOCK1; } continue {
++$i; }
print "Переменная \$i после BLOCK1: $i\n"; BLOCK2: {
$i = 1;
next BLOCK2; } continue {
++$i; } print "Переменная \$i после BLOCK2: $i\n";
Первый оператор print этого фрагмента кода напечатает значение переменной $i равным n, тогда как второй оператор print напечатает 2, так как при выходе из блока BLOCK2 будет выполнен оператор увеличения на единицу переменной $i из блока continue.
Замечание
Если в простом блоке операторов задан блок continue, то при нормальном завершении простого блока (без использования команд управления циклом) блок операторов continue также будет выполнен.
Блоки могут иметь метки, и в этом случае их называют именованными блоками. Подобные конструкции используются для реализации переключателей — конструкций, которые не определены в синтаксисе языка Perl. Существует множество способов создания переключателей средствами языка Perl. Один из них представлен в примере 5.18.
#! peri -w $var = 3; SWITCH: {
$casel = 1, last SWITCH if $var == 1;
$case2 = 1, last SWITCH if $var == 2;
$case3 = 1, last SWITCH if $var = 3;
$nothing = 1; }
После выполнения именованного блока операторов SWITCH переменная $casei будет равна 1, если $var равна i, $case2 будет равна 2, если $var равна 2 и, наконец, $case3 будет равна 3, если $var равна 3. В случае, если переменная $var не равна ни одному из перечисленных значений, то переменная $nothing будет равна i. Конечно, это простейший переключатель, разработанный всего лишь для демонстрации возможности быстрого создания переключателя в Perl. Для выполнения группы операторов в переключателе можно использовать не модификатор if, а оператор выбора if.
Блоки могут вложенными друг в друга. Именованные блоки и команды управления циклом, используемые для выхода из внутренних блоков, позволяют создавать достаточно прозрачные конструкции, реализующие сложные алгоритмы. Например, можно организовать бесконечный цикл без использования какого-либо оператора цикла:
$notempty = 0; $total = 0; INPUT: {
$line=<STDIN>; chop($line);
last INPUT if $line eq "END"; # Выход из бесконечного цикла ++$total;
redo INPUT if $line eq ""; ++$notempty; redo INPUT; }
Узнаете программу примера 5.16? Действительно, это реализация без оператора цикла программы ввода строк и подсчета общего числа введенных, а также непустых строк. Единственное, что нам пришлось добавить — еще одну команду redo в конце блока операторов.
Команды управления циклом
Команды управления циклом
Каждый цикл в программе завершается при достижении некоторого условия, определяемого самим оператором. В циклах while и for это связано с ложностью выражения-условия, а в цикле foreach с окончанием перебора всех элементов списка. Иногда возникает необходимость при возникновении некоторых условий завершить выполнение всего цикла, либо прервать выполнение операторов цикла и перейти на очередную итерации. Для подобных целей в языке Perl предусмотрены три команды last, next и redo, которые и называют командами управления циклом.
Синтаксис этих команд прост — ключевое слово, за которым может следовать необязательный идентификатор метки:
last ИДЕНТИФИКАТОР_МЕТКИ; next ИДЕНТИФИКАТОР_МЕТКИ; redo ИДЕНТИФИКАТОР_МЕТКИ;
Семантика этих команд также проста. Они изменяют порядок выполнения циклов, принятый по умолчанию в языке, и передают управление в определенное место программы, завершая выполнение цикла (last), переходя на следующую итерацию цикла (next) или повторяя выполнение операторов тела цикла при тех же значениях переменных цикла (redo). Место перехода задается меткой. Помните синтаксис операторов цикла? Каждый из них может быть помечен. Именно идентификаторы меток операторов цикла и используются в командах управления для указания места передачи управления.
Метка в программе Perl задается идентификатором, за которым следует двоеточие. В командах управления циклом используется именно идентификатор метки, а не метка.
Несколько слов о терминологии. Читатель, наверное, обратил внимание, что мы не называем команды управления циклом операторами. И это справедливо. Они не являются операторами, хотя могут использоваться как операторы. Их следует считать унарными операциями, результатом вычисления которых является изменение последовательности выполнения операторов. Поэтому команды управления циклом можно использовать в любом выражении Perl. Заметим, что их следует использовать в таких выражениях, где имеет смысл их использовать, например в выражениях с операцией "запятая":
open (INPUT_FILE, $file)
or warn ("Невозможно открыть $file: $!\n"), next FILE;
Приведенный оператор может являться частью программы, которая в цикле последовательно открывает и обрабатывает файлы. Команда next инициирует очередную итерацию цикла с меткой FILE, если не удалось открыть файл в текущей итерации. Обратите внимание, что она используется в качестве операнда операции "запятая". В таком контексте эта команда имеет смысл. Следующий оператор является синтаксически правильным, но использование в нем команды redo не имеет никакого смысла:
print "qu-qu", 5 * redo OUT, "hi-hi\n";
Результатом выполнения этого оператора будет повторение вычислений операторов цикла с меткой ODT, т. е. простое выполнение команды redo OUT.
Относительно команд управления циклом следует сказать, что к ним можно применять модификаторы, так как употребленные самостоятельно с завершающей точкой с запятой они рассматриваются как простые операторы: next if $a — 2;
Переход на следующую итерацию цикла осуществится только, если переменная $а равна 2.
Модификаторы простых операторов
Модификаторы простых операторов
Каждый простой оператор может быть снабжен модификатором, представляющим ключевое СЛОВО if, unless, while, until ИЛИ foreach, за которым следует выражение-условие. В самом операторе модификатор стоит непосредственно за выражением, составляющим простой оператор, перед завершающим символом точка с запятой. Каждый простой оператор может иметь только один модификатор. Семантически роль модификатора сводится к тому, что оператор вычисляется при выполнении условия, определяемого модификатором. Например, следующий оператор присваивания
$n = $l/$m if $т != 0;
с модификатором if будет выполнен при условии, что переменная $т не равна о. Общий синтаксис простого оператора с модификатором имеет следующий вид:
ВЫРАЖЕНИЕ ключ_слово_модификатора [(]ВЫРАЖЕНИЕ-УСЛОВИЕ [)];
Оператор безусловного перехода
Оператор безусловного перехода
Оператор безусловного перехода goto, возможно, самый спорный оператор. Много копий было поломано в дебатах о его целесообразности и полезности. Однако практически в любом языке программирования можно обнаружить оператор безусловного перехода. Не является исключением и язык Perl. В нем есть три формы этого оператора:
goto МЕТКА; goto ВЫРАЖЕНИЕ; goto &ПОДПРОГРАММА;
Первая форма goto МЕТКА передает управление на оператор с меткой МЕТКА, который может быть расположен в любом месте программы, за исключением конструкций, требующих определенных инициирующих действий перед их выполнением. К ним относятся цикл foreach и определение подпрограммы sub.
Замечание
Компилятор Perl не генерирует никаких ошибок, если в операторе goto задана не существующая метка, или он передает управление в конструкцию foreach или sub. Все ошибки, связанные с этим оператором, возникают во время выполнения программы.
Во второй форме оператора безусловного перехода goto ВЫРАЖЕНИЕ возвращаемым значением выражения должен быть метка, на которую и будет передано управление в программе. Эта форма оператора goto является аналогом вычисляемого goto языка FORTRAN:
@label = ("OUT", "IN");
goto $label[l]; •
В приведенном фрагменте кода выражение в операторе goto будет вычислено равным строке IN и именно на оператор с этой меткой будет передано управление.
Последняя форма оператора goto «ПОДПРОГРАММА обладает магическим свойством, как отмечают авторы языка. Она подставляет вызов указанной в операторе подпрограммы для выполняемой в данной момент подпрограммы. Эта процедура осуществляется подпрограммами AUTOLOAD (), которые загружают одну подпрограмму, скрывая затем, что на самом деле сначала была вызвана другая подпрограмма.
Описание оператора goto приведено нами исключительно для полноты изложения. В программах его следует избегать, так как он делает логику программы более сложной и запутанной. Намного лучше использовать структурированные команды управления потоком вычислений next, last и redo. Если в процессе программирования выяснится, что не обойтись без оператора безусловного перехода, то это будет означать только одно: на этапе проектирования программы она была не достаточно хорошо структурирована. Вернитесь снова к этапу проектирования и постарайтесь реструктурировать ее таким образом, чтобы не требовалось использовать оператор goto.
В этом разделе мы познакомились с основными операторами языка Perl, которые используются для написания программ. Узнали, что операторы могут быть простыми и составными.
Выполнением простых операторов можно управлять с помощью модификаторов, которые вычисляют простой оператор при выполнении некоторого условия. Некоторые модификаторы организуют повторное вычисление в цикле оператора, к которому они применены.
Составные операторы представляют собой операторы, управляющие потоком вычислений в программе. Они определяются в терминах блоков. К ним относятся операторы выбора и цикла. Команды управления циклом позволяют изменить порядок выполнения операторов цикла.
Операторы цикла
Операторы цикла
Известно, что для реализации любого алгоритма достаточно трех структур управления: последовательного выполнения, ветвления по условию и цикла с предусловием. Любой язык программирования предоставляет в распоряжение программиста набор всех трех управляющих конструкций, дополняя их для удобства программирования другими конструкциями: цепочки ветвления и разнообразные формы цикла с предусловием, а также циклы с постусловием.
Мы уже познакомились с операторами ветвления Perl, а теперь пришло время узнать, какие конструкции цикла можно применять в Perl. Их всего три: while, for и foreach. Все они относятся к классу составных операторов и, естественно, определяются в терминах блоков БЛОК.
Циклы while и until
Цикл while предназначен для повторного вычисления блока операторов, пока остается истинным задаваемое в нем выражение-условие. Его общий синтаксис имеет две формы:
МЕТКА while (ВЫРАЖЕНИЕ) БЛОК
МЕТКА while (ВЫРАЖЕНИЕ) БЛОК continue БЛОК1
Все операторы цикла могут быть снабжены не обязательными метками. В Perl метка представляет правильный идентификатор, завершающийся двоеточием ":". Она важна для команды перехода next, о которой мы поговорим в следующем разделе.
Оператор while выполняется по следующей схеме. Вычисляется выражения-условия ВЫРАЖЕНИЕ. Если оно истинно, то выполняются операторы БЛОК. В противном случае оператор цикла завершает свою работу и передает управление следующему после него оператору программы (цикл 1 примера 5.5). Таким образом, оператор цикла while является управляющей конструкцией цикла с предусловием: сначала проверяется условие завершения цикла, а потом только тело цикла, определяемое операторами БЛОК. Поэтому может оказаться, что тело цикла не будет выполнено ни одного раза, если при первом вхождении в цикл условие окажется ложным (цикл 3 примера 5.5).
Вместо ключевого слова while можно использовать ключевое слово until. В этом случае управляющая конструкция называется циклом until, который отличается от разобранного цикла while тем, что его тело выполняется, только если выражение условия ложно (цикл 2 примера 5.5).
# peri -w
# цикл 1
$i = 1;
while <$i•<= 3) {
$a[$i] = l/$i; # Присвоить значение элементу массива
++$i; >
print "Переменная цикла \$i = $i\n"; # $i = 4 print "Массив \@a: @a\n"; # @a = (I, 0.5. 0.333333333333333)
# цикл 2, эквивалентный предыдущему
$i = 1;
until ($i > 3) {
$a[$i] = l/$i; # Присвоить значение элементу массива
++$i; }
print "Переменная цикла \$i = $i\n"; # $i = 4 print "Массив \@a: @a\n"; # @a = (1, 0.5. 0.333333333333333)
# цикл З, тело цикла не выполняется ни одного раза
$i = 5;
while ($i-<= 3) {
$a[$i] = l/$i;
++$i; } print "Переменная цикла \$i = $i\n"; # $i = 5
# цикл 4, бесконечный цикл (не изменяется выражение условия)
$i = 1;
while ($i <= 3) {
$a[$i] = l/$i; } .
Обратим внимание на то, что в теле цикла должны присутствовать операторы, вычисление которых приводит к изменению выражения условия. Обычно это операторы, изменяющие значения переменных, используемых в выражении условия. Если этого не происходит, то цикл while или until будет выполняться бесконечно (цикл 4 примера 5.5).
Замечание
Цикл с постусловием реализуется применением модификатора while к конструкции do{), и рассматривался нами в разделе 5.2.2 " Модификаторы while и until".
Блок операторов БЛОК!, задаваемый после ключевого слова continue, выполняется всякий раз, когда осуществляется переход на выполнение новой итерации. Это происходит после выполнения последнего оператора тела цикла или при явном переходе на следующую итерацию цикла командой next. Блок continue на практике используется редко, но с его помощью можно строго определить цикл for через оператор цикла while.
Пример 5.6 демонстрирует использование цикла while для вычисления степеней двойки не выше шестнадцатой. В этом примере оператор цикла while функционально эквивалентен циклу for. Блок continue выполняется всякий раз по завершении очередной итерации цикла, увеличивая переменную $i на единицу. Он эквивалентен выражению увеличения/уменьшения оператора for.
# peri -w
# Вычисление степеней числа 2 $1 = I;
while ($i <= 16) {
print "2 в степени $i: ", 2**$i, "\n"; } continue {
++$i; f Увеличение переменной $ i перед выполнением следующей итерации }
Цикл for
При выполнении циклов while и until заранее не известно, сколько итераций необходимо выполнить. Их количество зависит от многих факторов: значений переменных в выражении условия до начала выполнения цикла, их изменении в теле цикла, виде самого выражения условия и т. п. Но иногда в программе необходимо выполнить заранее известное количество повторений определенной группы операторов. Например, прочитать из файла 5 строк и видоизменить их по определенным правилам. Конечно, можно такую задачу запрограммировать операторами цикла while и until, но это может выглядеть не совсем выразительно. В том смысле, что при прочтении программы придется немного "пошевелить" мозгами, прежде чем понять смысл оператора цикла. Для решения подобных задач с заранее известным числом повторений язык Perl предлагает специальную конструкцию цикла — цикл for:
МЕТКА for (ВЫРАЖЕНИЕ1; ВЫРАЖЕНИЕ2; ВЫРАЖЕНИЕЗ) БЛОК
ВЫРАЖЕНИЕ1 используется для установки начальных значений переменных, управляющих циклом, поэтому его называют инициализирующим выражением. Обычно это одна или несколько операций присваивания, разделенных запятыми.
ВЫРАЖЕНИЕ2 определяет условие, при котором будут повторяться итерации цикла. Оно, как и выражение-условие цикла while, должно быть истинным, чтобы началась следующая итерация. Как только это выражение становится ложным, цикл for прекращает выполняться и передает управление следующему за ним в программе оператору.
ВЫРАЖЕНИЕЗ отвечает за увеличение/уменьшение значений переменных цикла после завершения очередной итерации. Обычно оно представляет собой список выражений с побочным эффектом или список операций присваивания переменным цикла новых значений. Его иногда называют изменяющим выражением.
Алгоритм выполнения цикла for следующий:
1. Вычисляется инициализирующее выражение (ВЫРАЖЕНИЕ!).
2. Вычисляется выражение условия (вырАЖЕШЕ2). Если оно истинно, то выполняются операторы блока БЛОК, иначе цикл завершает свое выполнение.
3. После выполнения очередной итерации вычисляется выражение увеличения/уменьшения (ВЫРАЖЕНИЕЗ) и повторяется пункт 2.
Как отмечалось в предыдущем разделе, цикл for эквивалентен циклу while с блоком continue. Например, следующий цикл
for ($i = 1; $i .<= 10; $i++) { }
эквивалентен циклу while
$i =1;
while ($i <= 10) {
} continue {
$i++; }
Существует единственное отличие между этими двумя циклами. Цикл for определяет лексическую область видимости для переменной цикла. Это позволяет использовать в качестве переменных цикла локальные переменные, объявленные с помощью функции ту:
$i = "global";
for (my $i = 1; $i <= 3; $i++) {
print "Внутри цикла \$i: $i\n"; } print "Вне цикла \$i: $i\n ";
При выполнении этого фрагмента программы оператор печати будет последовательно отображать значения 1, 2 и з локальной переменной цикла $1. При выходе из цикла локальная переменная $i будет уничтожена и оператор печати вне цикла напечатает строку global — значение глобальной переменной $1, определенной вне цикла for.
Все три выражения цикла for являются необязательными и могут быть опущены, но соответствующие разделители ",-" должны быть оставлены. Если опущено выражение условия, то по умолчанию оно принимается равным Истина. Это позволяет организовать бесконечный цикл:
for (;;) {
}
Выход из такого цикла осуществляется командами управления, о которых речь пойдет в следующем параграфе.
Инициализировать переменную цикла можно и вне цикла, а изменять значение переменной цикла можно и внутри тела цикла. В этом случае инициализирующее и изменяющее выражения не обязательны:
$i = 1;
for (•; $i <= 3;) {
$i++; }
Совет
Хотя существует возможность изменения переменной цикла в теле цикла, не рекомендуется ею пользоваться. Цикл for был введен в язык именно для того, чтобы собрать в одном месте все операторы, управляющие работой цикла, что позволяет достаточно быстро изменить его поведение.
Цикл for позволяет использовать несколько переменных для управления работой цикла. В этом случае в инициализирующем и изменяющем выражениях используется операция запятая. Например, если мы хотим создать хеш, в котором ключам, представляющим цифры от 1 до 9, соответствуют значения этих же цифр в обратном порядке от 9 до 1, то эту задачу можно решить с помощью цикла for с двумя переменными цикла:
for ($j = 1, $k = 9; $k >0; $j++, $k—) {
$hash{$j} = $k; }
Этот же пример показывает, что в цикле for переменная цикла может как увеличиваться, так и уменьшаться. Главное, чтобы выражение условия правильно отслеживало условия продолжения итераций цикла.
Цикл for достаточно гибкая конструкция, которую можно использовать не только для реализации цикла с заранее заданным числом итераций. Он позволяет в инициализирующем и изменяющем выражениях использовать вызовы встроенных и пользовательских функций, а не только определять и изменять переменные цикла. Основное — чтобы изменялось выражение условия завершения цикла. Пример 5.7 демонстрирует именно такое использование цикла for.
# peri -w
for (print "Введите данные, для завершения ввода нажмите <Enter>\n"; <STDIN>;
print "Введите данные, для завершения ввода нажмите <Enter>\n") {
last if $_ eq "\n"; print "Ввели строку: $_"; }
В этом примере пользователь вводит в цикле строки данных. Перед вводом новой строки отображается подсказка с помощью функции print (), которая определена в изменяющем выражении цикла. Выражение условия представляет операцию ввода из файла стандартного ввода <STDIN>. Так как это выражение вычисляется всякий раз, когда цикл переходит на очередную итерацию, то на каждом шаге цикла программа будет ожидать ввода с клавиатуры. Выход из цикла осуществляется командой last, вызываемой в случае ввода пользователем пустой строки. Введенные данные сохраняются во встроенной переменной $_, причем в ней сохраняется и символ перехода на новую строку, являющийся признаком завершения операции ввода данных. Поэтому при вводе пустой строки на самом деле в переменной $_ хранится управляющая последовательность "\n", с которой и осуществляется сравнение для реализации выхода из цикла.
Пример 5.8 демонстрирует программу, читающую 3 строки файла егг.егг. Операция чтения из файла задается в инициализирующем и изменяющем выражении вместе с определением и изменением переменной цикла $1.
# peri -w
open (FF, "err.err") or die "Ошибка открытия файла";
for ($line=<FF>, $count = 1; $count <=3; $line=<FF>, $count++)
{
print "Строка $count:\n $line\n";
t ' • }
close(FILE);
Цикл foreach
Одно из наиболее частых применений циклов в языках программирования — организация вычислений с элементами массивов: найти максимальный элемент, распечатать элементы массива, выяснить, существует ли элемент массива, равный заданному значению, и т. п. Подобные задачи легко решаются с помощью циклов while и for. В примере 5.9 определяется максимальный элемент массива (в предположении, что он содержит числовые данные).
#! peri -w
@array = (1,-6,9,18,0,-10);
$max = $array[0]; ,
for ($i = 1; $i <= $farray; $i++) {
$max = $array[$i] if $array[$i] > $max; }
После выполнения программы примера 5.9 переменная $max будет иметь значение 18, равное максимальному элементу массива $аггау. Обратим внимание читателя на то, что в цикле for (как и в цикле while) доступ к элементам массива организуется с помощью индекса.
В Perl списки и массивы, являющиеся, по существу, также списками, являются столь полезными и часто используемыми конструкциями, что для организации цикла по их элементам в языке предусмотрен специальный оператор foreach, имеющий следующий синтаксис:
МЕТКА foreach ПЕРЕМЕННАЯ (СПИСОК) БЛОК
МЕТКА foreach ПЕРЕМЕННАЯ (СПИСОК) БЛОК continue БЛОК
Он реализует цикл по элементам списка список, присваивая на каждом шаге цикла переменной ПЕРЕМЕННАЯ значение выбранного элемента списка. Блок операторов continue выполняется всякий раз, как начинается очередная итерация, за исключением первой итерации, когда переменная $temp равна первому элементу списка. Список можно задавать или последовательностью значений, разделенных запятыми, или массивом скаляров, или функцией, возвращаемым значением которой является список. Определение максимального элемента массива можно переписать с циклом foreach (пример 5.10).
#! peri, -w
Sarray = (1,-6,9,18,0,-10) ; $max = $array[0]; foreach $temp (Sarray) (
$max = $temp if $temp > $max; } print "$max";
На каждом шаге цикла переменная $temp последовательно принимает значения элементов массива $аггау. Обратите внимание на внешний вид программы — в отсутствии индексов массива она стала лучше читаемой.
Отметим несколько особенностей цикла foreach. Прежде всего следует сказать, что ключевое слово foreach является синонимом ключевого слова for. Цикл из примера 5.10 можно было бы записать и так:
for $temp (@array) { # Ключевое слово foreach синоним for.
$max = $temp if $temp > $max; }
Однако, как нам кажется, использование foreach лучше отражает семантику этого оператора цикла, так как в самом ключевом слове уже отражена его сущность (for each — для каждого).
Следующая особенность оператора foreach связана с переменной цикла. По умолчанию эта переменная является локальной, область видимости которой ограничена телом цикла. Она создается только на время выполнения оператора foreach, доступна внутри тела цикла и уничтожается при выходе из цикла.
Обычно программисты, работающие на языке Perl, вообще не применяют в циклах foreach переменную цикла. Это связано с тем обстоятельством, что в отсутствии явно заданной переменной цикла Perl по умолчанию использует специальную переменную $_. На каждом шаге цикла именно она будет содержать значение элемента списка или массива. С учетом этого факта цикл foreach примера 5.10 можно переписать так:
foreach (@array) { # В качестве переменной цикла используется $_.
$гоах = $_ if $_ > $max;
} . l
Последняя особенность оператора foreach, которая также связана с переменной цикла, заключается в том, что фактически на каждом шаге выполнения цикла эта переменная является синонимом того элемента списка, значение которого она содержит. Это означает, что ее изменение в цикле приводит к изменению значения соответствующего элемента списка. Это свойство цикла foreach удобно для изменения значений элементов списка. Отметим, что его можно применять к спискам, хранящимся в массивах. Например, возвести в квадрат каждый элемент списка можно следующим оператором foreach:
foreach $temp (@array) {
$temp **= 2; }
Список, по элементам которого организуется цикл, может быть задан не только явно конструктором или переменной массива, но и функцией, возвращаемым значением которой является список. Канонический способ печати хеш-массива в упорядоченном порядке представлен в примере 5.11.
# peri -w %array = {
blue => 1,
red => 2,
green => 3,
yellow => 3 ); foreach (sort keys %array) {
print "$_\t => $array{$_}\n"; } '
Эта программа напечатает пары ключ/значение хеш-массива %аггау в соответствии с алфавитным порядком его ключей:
blue => 1
green => 3
red => 2
yellow => 3
Замечание
Цикл foreach выполняется быстрее аналогичного цикла for, так как не требует дополнительных затрат на вычисление индекса элемента списка
Простые операторы
Простые операторы
Простой оператор представ
Perl является императивным языком программирования: его программа состоит из последовательности операторов, определяющих некоторые действия. Оператор — это завершенная инструкция интерпретатору на выполнение определенного действия. Все операторы языка Perl делятся на простые и составные. Простой оператор представляет собой выражение, возможно, снабженное модификатором. Составной оператор определяется в терминах блоков.
Каждый оператор Perl имеет возвращаемое значение. Для простого оператора — это значение вычисляемого в нем выражения, для составного оператора — значение последнего вычисленного в нем оператора.
Операторы обрабатываются в той последовательности, в которой они встречаются в программе. Однако среди множества допустимых операторов языка Perl, есть группа операторов, которая изменяет последовательность выполнения операторов в программе.
ляет собой любое выражение, завершенное точкой с запятой ";". Символ точка с запятой обязателен. Он может отсутствовать, только если простой оператор является последним оператором в блоке, специальной синтаксической конструкции, о которой мы поговорим чуть-чуть позже.
Основное назначение простого оператора — вычисление выражения с побочным эффектом, который связан с изменением значения некоторых переменных в выражении при его вычислении. Обычно он реализуется операциями увеличения/уменьшения на единицу (++, --):
$п++; # Переменная $п увеличивается на единицу. —$п**2; # Переменная $п уменьшается на единицу.
Никакие другие операции Perl не вызывают побочных эффектов в выражении, если не считать операции присваивания (простое и составное), результатом вычисления которой является изменение значения левого операнда. Вызовы функций также могут приводить к побочным эффектам, но об этом подробнее мы расскажем в главе 11.
Простой оператор Perl может содержать выражение и без побочного эффекта. Все равно он будет рассматриваться интерпретатором как допустимый оператор, не выполняющий никакого действия, а только вычисляющий значение выражения. Однако если установлен флаг (-w) отображения интерпретатором предупреждающих сообщений, то будет получено сообщение о бесполезности использования соответствующей операции в void-контексте. Например, при выполнении оператора
($n*$m)**4 + 6;
будет отображено сообщение
Useless use of addition in void context at D:\PERL\EXAMPLE3.PL line 4.
Отметим, что в сообщении упоминается о последней выполненной операции в выражении.
Замечание
Подобное сообщение будет отображаться, даже если в сложном выражении присутствует операция, вызывающая побочный эффект. Сообщение не отображается только в случае выражения, составленного из одних операций уменьшения /увеличения, или выражения присваивания.
Читатель спросит, какой прок в операторе, не выполняющем никакого действия. Его можно использовать для задания возвращаемого пользовательской функцией значения. Забегая вперед, скажем, что если в функции явно не указано в операторе return о возвращаемое значение, то по умолчанию Perl считает таковым значение последнего вычисленного оператора. Именно это обстоятельство и используется многими программистами для определения возвращаемого значения:
sub raySub {
какие-то операторы condition == true ? "Успех" : "Провал"; # Последний оператор * '
Последний оператор функции mysub вычисляет операцию выбора, в которой второй и третий операнды представлены просто строковыми литералами — выражениями без побочного эффекта. Результат вычисления одного из них и является возвращаемым значением функции.
Составные операторы
Составные операторы
Составные операторы — это второй тип операторов языка Perl. С их помощью реализуют ветвления в программе и организуют циклические вычисления. Эти операторы, в отличие от аналогичных операторов других языков программирования, определяются в терминах блоков — специальном понятии языка Perl, задающим область видимости переменных. Именно с блоков мы и начнем изучение составных операторов.
Блоки
Блок — последовательность операторов, определяющая область видимости переменных. В программе блок обычно ограничен фигурными скобками {...}. Определяя синтаксис составных операторов, мы будем иметь в виду именно такой блок — последовательность операторов в фигурных скобках и обозначать его БЛОК. Интерпретатор рассматривает БЛОК как один оператор, вычисляемым значением которого является значение последнего выполненного оператора блока. Это означает, что там, где можно использовать один оператор, можно использовать и БЛОК. Такая ситуация встречается при использовании функции тар(). Она выполняет определяемый ее первым параметром оператор для всех элементов списка, заданного вторым параметром. Значение каждого элемента списка при вычислениях временно присваивается встроенной переменной $_. Возвращает эта функция список вычисленных значений оператора:
@rez = map $_ **= 2, @array; # Список квадратов элементов массива.
В качестве первого параметра этой функции можно использовать БЛОК. Следующий оператор также вычисляет список квадратов элементов массива @аггау, одновременно подсчитывая количество его элементов:
@rez = map { ++$kol; $_ **= 2} @array; # Список квадратов элементов
# массива и подсчет их количества.
Обратите внимание, что возвращаемым значением блока операторов в этом примере является значение последнего оператора блока, которое и попадает в возвращаемый функцией тар () список.
Замечание
Блоки в Perl не ограничиваются только последовательностью операторов в фигурных скобках. Иногда блок может быть ограничен содержащим его файлом. Например, файлом, содержащим используемый в программе модуль Perl, или файлом самой программы.
Как сказано в начале этого раздела, блок определяет область видимости переменных. Это означает, что в блоке можно создать переменные, обращаться к которым можно только из операторов, расположенных в этом блоке. Пока мы в блоке, мы можем присваивать им новые значения, использовать в вычислениях и т. п., но как только мы вышли из блока, мы теряем с ними "связь", они становятся "не видимыми". Такие переменные еще называют локальными переменными.
Локальные переменные создаются с помощью функции ту (). Ее параметром является список переменных, область видимости которых ограничена блоком, в котором вызывается эта функция. Если список переменных состоит из одной переменной, то скобки не обязательны. Созданные функцией my о переменные называются также лексическими переменными, так как область их действия ограничена фрагментом текста программы — блоком операторов.
В языке Perl можно создавать другой тип локальных переменных, область действия которых определяется динамически во время выполнения программы. Они создаются функцией local о и называются локальными динамическими переменных. Однако именно переменные, созданные функцией ту (), являются "истинными" локальными переменными: они создаются при входе в блок и уничтожаются при выходе из него (хотя существуют ситуации, когда Perl не уничтожает локальную лексическую переменную при выходе из ее области действия). Функция local о всего лишь временно сохраняет старое значение глобальной переменной при входе в блок и восстанавливает его при выходе из него.
(Более подробно лексические и динамические переменные рассматриваются в .)
Локальные переменные удобны для создания временных переменных, которые нигде больше не будут использоваться в программе, а только в одном определенном месте. Например, при отладке части кода часто приходится создавать временные переменные и выводить на печать их значения. Локальные переменные могут иметь такие же имена, как и глобально используемые переменные. Это не приводит к конфликту. После завершения операторов блока значение глобальной переменной имеет то же значение, которое она имела до начала выполнения операторов блока (пример 5.4).
# peri -w
$var = "outer"; # Глобальная переменная $var $glob = "glob"; # Глобальная переменная $glob my $lex = "outer_l"; # Лексическая переменная $1ех {
my($var) = "inner"; # Внутренняя переменная $var my($varl) = "inner_l"; # Внутренняя переменная $varl print "В блоке \$var = $var\n"; # Напечатает inner print "В блоке \$varl = $varl\n"; # Напечатает inner_l print "В блоке \$lex = $lex\n"; # Напечатает outer_l print "В блоке \$glob = $glob\n"; # Напечатает glob }
print "Вне блока \$var = $var\n"; # Напечатает outer print "Вне блока \$1ех = $lex\n"; # Напечатает outer_l print "Вне блока \$varl = $varl\n"; # Напечатает пустую строку ""
Программа примера 5. 4 демонстрирует области видимости лексических переменных. Внутри блока {...} "видны" переменные, созданные вне блока: и глобальные, и лексические ($giob, $iex), если только они не переопределены внутри блока ($var). При выходе из внутреннего блока восстанавливаются значения переменных, которые были переопределены внутри блока ($var). Доступ к локальным переменным блока извне невозможен ($vari).
Операторы ветвления
Операторы программы Perl выполняются последовательно в порядке их расположения в программе. Для реализации простых алгоритмов этого вполне достаточно. Однако большинство реальных алгоритмов не укладываются в такую линейную схему. Практически всегда при реализации любого алгоритма возникают ситуации, когда одну группу операторов надо выполнить только при выполнении определенных условиях, тогда как другую группу при этих же условиях вообще не следует выполнять. В языке Perl для организации подобного ветвления в программе предусмотрены операторы if, которые мы и будем называть операторами ветвления.
Эти операторы вычисляют выражение, называемое условием, и в зависимости от его истинности или ложности выполняют или не выполняют некоторый блок операторов. Это означает, что выражения условия во всех операторах ветвления вычисляются в булевом контексте.
Иногда приходится делать выбор на основе проверки нескольких различных условий. Для подобных цепочек ветвлений существует специальная форма оператора if, реализующая множественные проверки.
В языке существует три формы оператора ветвления if:
if (ВЫРАЖЕНИЕ) БЛОК
if (ВЫРАЖЕНИЕ) БЛОК1 else БЛОК2
if (ВЫРАЖЕНИЕ!) БЛОК1 elsif (ВЫРАЖЕНИЕ2) БЛОК2 ... else БЛОКп
Обратим внимание читателя еще раз на тот факт, что все они определяются в терминах блоков операторов, заключенных в фигурные скобки, поэтому даже если в блоке содержится один оператор, он должен быть заключен в фигурные скобки. Такой синтаксис составных операторов Perl может оказаться не совсем привычным для программистов на языке С, в котором фигурные скобки в случае одного оператора в блоке не обязательны.
Первый оператор if реализует простейшее ветвление. Если ВЫРАЖЕНИЕ истинно, то выполняются операторы из БЛОК, в противном случае БЛОК просто пропускается:
$var = 10;
if ( $var -= 5 ) {
print "Переменная \$var - $var"; }
Обратите внимание, что в этом примере ВЫРАЖЕНИЕ представляет операцию составного присваивания. Это может показаться необычным, так как в большинстве языков программирования здесь требуется выражение, возвращающее булево значение. В Perl можно использовать любое выражение, в том числе и присваивание. Результат его вычисления интерпретируется в булевом контексте: если вычисленное значение равно о или пустой строке "", то оно трактуется как Ложь, иначе — Истина. Возвращаемым значением операции присваивания является значение, присвоенное переменной левого операнда. В нашем примере это число 5, следовательно в булевом контексте оно трактуется как Истина, а поэтому оператор печати print будет выполнен. Если перед выполнением оператора if переменная $var будет равняться 5, то выражение условия будет вычислено равным Ложь и все операторы блока будут просто пропущены.
Обычно выражение условия представляет собой сложное выражение, составленное из операций отношения, связанных логическими операциями. Использование операции присваивания в выражении условия оператора if не совсем типично. Здесь мы его использовали, чтобы подчеркнуть то обстоятельство, что в Perl любое правильное выражение может быть использовано в качестве выражения условия, которое вычисляется в булевом контексте. Вторая форма оператора if используется, когда необходимо выполнить одну группу операторов (БЛОК!) в случае истинности некоторого выражения (ВЫРАЖЕНИЕ), а в случае его ложности — другую группу операторов (влок2):
if ($var >= 0} # ВЫРАЖЕНИЕ {
print "Переменная неотрицательна."; # БЛОК1, если ВЫРАЖЕНИЕ истинно } else {
print "Переменная отрицательна."; # БЛОК2, если ВЫРАЖЕНИЕ ложно }
По существу, первая форма оператора if эквивалентна второй форме, если БЛОК2 не содержит ни одного оператора.
Последняя, третья форма оператора if реализует цепочку ветвлений. Семантика этого оператора такова. Выполняются операторы из БЛОК!, если
ИСТИННО ВЫРАЖЕНИЕ!. ЕСЛИ ОНО ЛОЖНО, ТО ВЫПОЛНЯЮТСЯ Операторы ИЗ БЛОК2
в случае истинности выражение2. Если и оно ложно, то проверяется ВЫРАЖЕНИЕ З и т. д. Если ни одно из выражений условия оператора if не истинно, то выполняются операторы блока, определяемого после ключевого слова else в случае его наличия. В противном случае выполняется следующий после оператора if оператор программы. При выполнении следующего оператора ветвления if
if( $var < 0) { # ВЫРАЖЕНИЕ!
print "Переменная отрицательна"; i БЛОК! } elsif ( $var == 0) { # ВЫРАЖЕНИЕ2
print "Переменная равна нулю"; # БЛОК2 } else {
print "Переменная положительна"; # БЛОКЗ }
сначала проверяется условие отрицательности переменной $var. Если значение переменной строго меньше нуля (ВЫРАЖЕНИЕ1), то печатается сообщение из БЛОК! и оператор завершает свою работу. Если значение переменной не меньше нуля, то оно проверяется на равенство (ВЫРАЖЕНИЕ2) и в случае истинности выполняется оператор печати из блока операторов elsif (влок2). Если проверка на равенство нулю дала ложный результат, то выполняется оператор печати из блока операторов else (БЛОКЗ).
Замечание
Ключевое слово else вместе со своим блоком операторов может быть опущено.
В операторе if со множественными проверками может быть сколько угодно блоков elsif, но только один блок else.
Так как все операторы ветвления определяются в терминах блоков операторов, то не возникает двусмысленности при определении, какие операторы в какой части выполняются.
При работе с операторами ветвления важно помнить, что только один блок операторов будет выполнен — тот, для которого истинно соответствующее выражение условия.
Во всех операторах ветвления ключевое слово if может быть заменено на unless. В этом случае проверка выражения условия осуществляется на его ложность. Последний оператор if можно записать и так:
unless( $var >= 0} { # ВЫРАЖЕНИЕ!
print "Переменная отрицательна"; # БЛОК! } elsif ( $var == 0) { # ВЫРАЖЕНИЕ2
print "Переменная равна нулю"; # БЛОК2 } else {
print "Переменная положительна"; # БЛОКЗ }
При этом нам пришлось заменить ВЫРАЖЕНИЕ! на противоположное по смыслу.
Замечание
Все операторы if (unless) могут быть вложенными, т. е. в любом их блоке можно свободно использовать другие операторы ветвления.
Какие из следующих операторов являются
Упражнения
Какие из следующих операторов являются простыми, а какие составными: "abc" if 1; if ($a) { print $a;} do{ $а++; $Ь—;} until $b; while( $a eq "а") { $а—;}
Найдите ошибку в программе:
# peri -w $а = "true";
$b = "false";
if ($a) $a = $b;
elsif ($b) $b == $a;
Напишите программу, которая по заданному числу STEP печатает лесенку из STEP ступеней (каждая следующая ступень на один символ "-" шире предыдущей):
I (первая •• ступень}
I (вторая ступень)
I (третья ступень)
Напишите программу, которая во вводимой пользователем строке подсчитывает количество слов, количество не пробельных символов и количество пробельных символов. Словом считать непрерывную последовательность алфавитно-цифровых символов, ограниченных пробельными символами ("\n", "\t", " "). Для завершения программы пользователь должен ввести пустую строку.
5. Напишите программу, которая читает целую величину ROW и печатает первые ROW строк треугольника Паскаля:
1
1 1
121
1331
14641
Функция print
Функция print
Функция print наиболее часто используемая функция для вывода информации из сценария Perl. Ее синтаксис имеет следующий вид:
print ДЕСКРИПТОР СПИСОК;
Здесь ДЕСКРИПТОР представляет дескриптор файла, в который функция выводит строковые данные, представленные списком вывода список. Он может состоять из переменных, элементов массивов и выражений, вычисляемых как строковые данные. Дескриптор файла создается функцией open 0,0 которой мы поговорим в следующей главе. Он может быть опущен, и в этом случае вывод осуществляется в стандартный файл вывода STDOUT, если только функцией select о не выбран другой файл вывода по умолчанию. Как уже отмечалось ранее, обычно стандартное устройство вывода — экран монитора компьютера.
Функция print при выводе своего списка не завершает его символом новой строки "\п". Это означает, что следующая функция print начнет вывод на экран непосредственно после последнего выведенного предыдущей функцией print символа. Если такое поведение не желательно, то следует список вывода каждой функции print явно завершать строкой, содержащей символ новой строки, или включать его последним символом последнего элемента списка вывода. Пример 6.6 демонстрирует вывод с помощью функции print.
#! peri -w
print "String 1:";
print "String 2:\n";
print "String 3:", "\n";
print STDOUT "String 4:\n";
print FILEOUT "String 4:\n";
Вывод первых четырех функций print примера 6.6 представлен ниже:
String I:String 2: String 3: String 4:
Вторая функция print начинает свой вывод на той же строке, на которой завершила вывод первая функция, в которой в списке вывода нет символа перехода на новую строку. В четвертой функции явно указан дескриптор стандартного файла вывода STDOUT. Относительно пятой функции скажем, что она ничего ни в какой файл, определенный дескриптором FILEOUT, не выведет, так как с этим дескриптором не связан никакой файл. Для этого следовало бы до выполнения последней функции print открыть файл функцией open о и связать с ним дескриптор FILEOUT. Мы отложим эти вопросы до следующей главы.
Функция print, как и большинство других функций, определенных в языке Perl, является списковой операцией, и все элементы списка вывода вычисляются в списковом контексте. Это обстоятельство следует учитывать при использовании в качестве элементов списка вывода выражений с вызовами подпрограмм.
Все, что было сказано относительно списковых операций и их использования в качестве термов выражений в главе 4, относится, естественно, и к функции print. Если ее параметры, включая дескриптор файла, заключены в круглые скобки, то такая синтаксическая конструкция считается термом и в выражении имеет наивысший приоритет вычисления. Например, следующий оператор
print ($m + $n) ** 2;
напечатает сумму значений переменных $т и $п, а не их сумму, возведенную в квадрат. Компилятор peri, обнаружив после лексемы print левую круглую скобку, найдет правую круглую скобку и будет рассматривать их содержимое как список параметров функции print. А так как такая конструкция есть терм, то сначала будет выполнена операция печати суммы значений переменных, а потом результат этой операции (Истина =1) будет возведен в квадрат. Добавление необязательного дескриптора стандартного файла вывода STDOUT исправит подобную ошибку:
print STDOUT ($m + $n) ** 2; # Выведет ($m + $n) ** 2
Если в функции печати print не задан список вывода, то она по умолчанию выводит содержимое специальной переменной $_ в файл, определенный параметром ДЕСКРИПТОР:
print; # Выводится содержимое переменной $_ на экран монитора, print STDOUT; # Эквивалентен предыдущему оператору, print FILEOUT; # Выводится содержимое переменной $_ в файл # с дескриптором FILEOUT
В этой главе мы познакомились с основными возможностями ввода/вывода, предоставляемыми языком Perl. Узнали, как легко и просто можно выполнить команду операционной системы и получить результаты ее вывода на экран монитора непосредственно в программу Perl. Операция <> позволяет не только считывать записи внешних файлов, но и автоматически обрабатывать содержимое нескольких файлов, заданных в командной строке при запуске сценария Perl. Эта же операция позволяет осуществлять поиск файлов, чьи имена удовлетворяют заданному шаблону. Для вывода информации из программы используется функция print о, которая может выводить ее не только на экран монитора (стандартное устройство вывода), но также и во внешний файл.
Операция ()
Операция ()
Для нашего читателя эта операция не является совсем уж новой. Несколько слов о ней было сказано в части 4; в некоторых примерах мы ее использовали для ввода пользователем данных в программу Perl. Основное ее назначение — прочитать строку из файла, дескриптор которого является операндом этой операции. (Операнд операции о расположен внутри угловых скобок.) Мы не будем сейчас объяснять, что такое дескриптор файла, зачем он нужен и какую функцию выполняет в программах Perl. Эти вопросы будут нами подробно рассмотрены в следующей главе, посвященной работе с файлами. Здесь же мы остановимся на специальном случае использования этой операции — операции с пустым операндом 0. В этом случае ввод осуществляется или из стандартного файла ввода, или из каждого файла, перечисленного в командной строке при запуске программы Perl. Но прежде чем перейти к описанию функционирования операции ввода с пустым операндом, остановимся на некоторых понятиях, необходимых для понимания дальнейшего.
Для обеспечения единообразия работы программ Perl в разных операционных системах при их запуске открывается несколько стандартных файлов. Один из них предназначен для ввода данных в программу и связан со стандартным устройством ввода — клавиатурой. Этот файл и называется стандартным файлом ввода и имеет дескриптор STDIN. Для вывода информации из программы создается стандартный файл вывода, также связанный со стандартным устройством вывода операционной системы, которым является экран монитора компьютера. Этому стандартному файлу назначается дескриптор STDOUT. Для отображения разнообразных сообщений о возникающих в процессе выполнения программы ошибках создается стандартный файл ошибок, который связан со стандартным устройством вывода. Этот файл имеет дескриптор STDERR. Эти файлы не надо создавать и открывать — они уже существуют, когда наша программа начинает выполняться. Иногда их называют предопределенными файлами ввода/вывода. Таким образом, если мы, например, говорим о том, что ввод осуществляется из стандартного файла (или стандартного устройства), мы имеем в виду стандартный файл ввода с дескриптором STDIN.
При запуске программы Perl в системе UNIX или из командной строки Windows ей можно передать параметры. Эти параметры задаются после имени файла, содержащего программу Perl, и отделяются от него и друг от друга пробелами:
peri program.pl parl par2 рагЗ
Параметрами могут быть ключи (обычно символ с лидирующим дефисом, например, -v), которые устанавливают определенные режимы работы программы, или имена файлов, содержимое которых должна обработать программа. Все передаваемые в программу параметры сохраняются в специальном встроенном массиве @ARGV. Если не передается ни одного параметра, то этот массив пуст.
Операция о без операнда, употребленная в циклах while и for, при первом своем вычислении проверяет, пуст ли массив @ARGV. Если он пуст, то в первый элемент этого массива $ARGV[O] заносится символ "-" и операция ожидает ввода пользователя из стандартного файла ввода STDIN. Если массив @ARGV не пуст, то он содержит параметры, переданные программе при ее запуске. Операция о трактует каждый из них как имя файла и в цикле передает в программу последовательно все строки всех файлов, указанных в командной строке. Рассмотрим простейшую программу (пример 6.4), состоящую из одного цикла while с операцией <>, и рассмотрим ее поведение при разных способах запуска.
i! peri -w
while ($line = <>) {
print $line; }
При ее запуске без параметров она будет ожидать ввода пользователя с клавиатуры и в цикле распечатывать вводимые им строки, пока пользователь не завершит ввод комбинацией клавиш <Ctrl>+<Z>, интерпретируемой как конец файла.
Если при запуске передать ей имя файла, например, файла, содержащего саму же программу,
peri examp6_4.pi examp6_4.pi
то программа примера 6.4 распечатает его содержимое:
f! peri -w
while ($line = <>) {
print $line; }
Замечание
Предполагается, что программа примера 6.4 сохранена в файле с именем examp6_4.pl.
Если эту же программу запустить, задав в командной строке дважды имя файла программы,
peri examp6_4.pl examp6_4.pl examp6_4.pl
то программа дважды распечатает свой собственный текст.
Замечание
В операционной системе Windows в именах файлов можно использовать пробелы. Для передачи в программу Perl файла с таким именем его следует заключать в двойные кавычки: "Name with blanks.dat".
При выполнении операции ввода из файла встроенная переменная $. на каждом шаге цикла хранит номер прочитанной строки файла. В случае задания нескольких имен файлов в командной строке при последовательном вводе их строк операцией о эта переменная продолжает увеличивать свое значение при переходе на чтение строк очередного файла, т. е. она рассматривает содержимое всех файлов как один-единственный файл.
Операцию о и массив @ARGV можно совместно использовать для ввода в программу содержимого нескольких файлов, не связывая их с заданием имен файлов в командной строке. В любом месте программы перед первым использованием в цикле операции ввода <> можно в массив SARGV занести имена файлов, содержимое которых необходимо обработать:
@ARGV = ("filel.dat", "file2.dat", "file3.dat"); for (;<>;) {
Операторы обработки строк файлов }
Этот фрагмент программы в цикле for последовательно обработает строки трех файлов filel.dat, file2.dat и file3.dat. Здесь же продемонстрирована еще одна интересная особенность операции ввода о. Обычно прочитанная этой операцией строка присваивается скалярной переменной, как это происходило в примере 6.4, но если эта операция одна представляет выражение условия цикла, то результат ее выполнения сохраняется в специальной встроенной переменной $_. Цикл while программы примера 6.4 можно записать и так:
while (<>) ( print;
}
Здесь также используется то обстоятельство, что функция print без параметров по умолчанию выводит содержимое переменной $_.
Если мы хотим передать в программу некоторые ключи, устанавливающие режим ее работы, то в начале программы следует поместить цикл, который проверяет содержимое массива @ARGV на наличие ключей в командной строке вызова программы. Один из способов подобной проверки приводится в примере 6.5, где предполагается, что программе могут быть переданы ключи
-d, -s и -е.
f! peri -w
while ($_ = $ARGV[0], / ^ -/) {
if(/ ^ -d/) { print $ARGV[0],"\n";}
if(/ ^ -s/) { print $ARGV[0]-, "\n"; }
1£(/ ^ -е/) { print $ARGV[0],"\n"; }
shift; }
При вычислении выражения условия цикла while осуществляется присваивание переменной $_ значения первого элемента массива @ARGV и проверка присутствия дефиса "-" в качестве первого символа содержимого этой переменной (операция / Л -/). Операторы if проверяют содержимое переменной $_ на соответствие известным ключам и отображают их. (В реальных программах в этих операторах обычно определяют некоторые переменные, которые в дальнейшем используются для выполнения действий, присущих соответствующим ключам.) Функция shift удаляет из массива @ARGV первое значение, сдвигая оставшиеся в нем элементы на одну позицию влево: второй становится первым, третий вторым и т. д. Цикл повторяется до тех пор, пока переданные через командную строку параметры начинаются с дефиса. Еще одно применение операции <> связано с получением в программе имен файлов определенного каталога, удовлетворяющих заданному шаблону. Если в качестве операнда этой операции используется шаблон имен файлов, то в скалярном контексте она возвращает первое найденное имя файла в текущем каталоге, в списковом контексте — список имен файлов, удовлетворяющих заданному шаблону. (В шаблоне можно использовать метасимволы: * для произвольной цепочки символов, ? для произвольного одиночного символа.) Если в каталоге не найдены файлы с именами, удовлетворяющими шаблону, то операция возвращает неопределенное значение. Например, выполнение следующей операции
$first = <*.pl>;
приведет к сохранению в переменной $ first имени первого файла из списка всех файлов текущего каталога с расширением pi, если таковые файлы в каталоге есть, иначе эта переменная будет иметь неопределенное значение. В списке файлы упорядочены в алфавитном порядке. Эта же операция в списковом контексте
gfiles = <*.pl>;
возвращает список всех файлов с расширением pi. После выполнения этой операции элементы массива @files содержат имена всех файлов с расширением pi.
Замечание
Имена подкаталогов текущего каталога считаются файлами без расширения. Например, в возвращаемом операцией <*. *> списке файлов будут содержаться и имена подкаталогов текущего каталога.
Если при задании шаблона файла явно указать каталог, то эта операция возвратит список файлов из указанного каталога, имена которых удовлетворяют заданному шаблону. Например, операция
@files = </perl/*.pl>;
сохранит в массиве @flies имена всех файлов каталога /peri с расширением pi.
Замечание
В системе Windows эта операция найдет все файлы с расширением р! в каталоге /peri текущего диска. Для задания конкретного диска следует использовать принятый в Windows синтаксис для полного имени файла: <d: /peri/*. *>. Эта операция возвратит список всех файлов каталога /peri, расположенного на диске d:.
При использовании этой операции в выражении условия цикла while или for она последовательно на каждом шаге цикла возвращает очередное имя файла, удовлетворяющее заданному шаблону:
while ($file = <*.pl>) (
print "$file\n"; }
Употребленная в выражении условия самостоятельно, эта операция возвращает очередное имя файла в переменной $_. Например, предыдущий фрагмент можно переписать следующим образом:
while (<*.pl>) {
print $_, "\п"; }
Операция получения имен файлов, соответствующих заданному шаблону, реализуется с помощью внутренней функции glob, единственным параметром которой является шаблон имен файлов. Эту функцию можно использовать самостоятельно для получения соответствующих имен файлов:
• @scripts = glob "*.pl";
В скалярном контексте она возвращает имя первого файла, удовлетворяющего заданному шаблону, в списковом — список имен всех файлов. Употребленная без параметра, она использует в качестве параметра специальную переменную $_.
Операция ввода команды
Операция ввода команды
Ни одна программа не может функционировать сама по себе, не получая и не посылая информацию во внешнюю среду. Perl предоставляет несколько способов получения программой данных извне и вывода информации из выполняющегося сценария. В процессе функционирования программы может потребоваться выполнить некоторую команду операционной системы и проанализировать результаты ее выполнения, прочитать данные из внешнего файла или группы файлов, записать результаты вычислений во внешний файл или отобразить их на экране монитора — все эти действия реализуются разнообразными операциями и функциями языка Perl.
Простейшее взаимодействие с операционной системой, в которой выполняется программа Perl, реализуется операцией заключения строки данных в обратные кавычки. Содержимое такой строки передается на выполнение операционной системы, которая возвращает результат выполнения команды в эту же строку.
Для чтения из файла используется операция "ромб" о, которой в качестве операнда передается дескриптор файла. В этой главе мы не будем обсуждать ввод из файла через его дескриптор, отнеся рассмотрение этого вопроса в следующую главу, полностью посвященную работе с файлами. Здесь мы расскажем о том, как работает операция "ромб" в случае отсутствия операнда, представляющего дескриптор файла. В этом случае эта операция может читать записи из стандартного файла ввода STDIN или получать информацию, передаваемую программе через командную строку.
Для отображения в стандартный файл вывода STDOUT используется уже знакомая нам функция print, которая, однако, может выводить информацию и в файл, определенный своим дескриптором.
Заключенная в обратные кавычки "•"• строка символов является всего лишь удобной формой записи операции ввода команды операционной системы qx{}, с которой мы уже знакомы (см. ).
Когда интерпретатор Perl встречает строковый литерал в обратных кавычках, он осуществляет подстановку в нее значений скалярных переменных и переменных массивов и передает получившуюся строку, как команду, на выполнение операционной системе. Последняя выполняет ее и возвращает в строковый литерал результаты вывода команды на стандартное устройство вывода, которым обычно является экран монитора. В связи с таким "поведением" строкового литерала в обратных кавычках его иногда называют псевдолитералом.
Операция ввода команды различает скалярный и списковый контексты, в которых она может выполняться. В скалярном контексте возвращается одна строка, содержащая весь вывод на экран монитора выполненной команды, включая символы новой строки в случае многострочного вывода. В списковом контексте возвращается список значений, каждое из которых содержит одну строку вывода. Пример 6.1 демонстрирует использование операции ввода команды в соответствующих контекстах.
#! peri -w
$command = "dir";
$scalar = ~$command'; # Скалярный контекст. ,
@list = '$command~; # Списковый контекст.
print $scalar;
print $list[0], $list[lj;
При выполнении операции заключения в кавычки сначала осуществляется подстановка значения скалярной переменной $ command, а потом полученная строка передается на выполнение операционной системы. Переменная $scaiar (скалярный контекст) содержит весь вывод на экран монитора содержимого текущего каталога, поэтому при ее печати мы увидим все, что вывела команда dir. Когда результаты выполнения команды присваиваются массиву @iist (списковый контекст), то каждая строка вывода команды становится элементом массива, поэтому последний оператор печати примера 6.1 выводит первую и вторую строки.
В списковом контексте разбиение вывода команды операционной системы на элементы списка осуществляется в соответствии со значением встроенной переменной $/, которое используется в качестве разделителя. По умолчанию эта переменная содержит символ конца строки "\п" — поэтому и разбиение на элементы происходит по строкам. Присваивая этой переменной новое значение, мы тем самым определяем новое значение разделителя, которое будет использоваться при формировании элементов списка. Разделителем может быть любая последовательность символов. Например, в примере 6.2 в качестве разделителя задается строка "<КАТАЛОГ>".
§! peri -w
$/ = "<КАТАЛОГ>";
@list = ~dir s ; t Списковый контекст.
print $list[l], $list[2];
Теперь, в отличие от примера 6.1, элемент массива $iist[ 0j содержит не первую строку вывода команды dir, а вывод команды до первой встретившейся в нем последовательности символов "<КАТАЛОГ>". Аналогично, элемент $list[l] содержит вывод команды до следующей встретившейся последовательности СИМВОЛОВ "<КАТАЛОГ>" И Т. Д.
Команда, содержащаяся в псевдолитерале, выполняется всякий раз, как вычисляется этот псевдолитерал. Встроенная переменная $? содержит числовое значение состояния выполненной команды.
(Об интерпретации значений встроенной переменной $ ? см. часть 14 .)
Хотим обратить внимание читателя еще раз на тот факт, что операция ввода команды возвращает вывод на стандартное устройство вывода операционной системы. При выполнении команды можно направить ее вывод на другое устройство, например, в файл. Для этого в строке после имени команды и всех необходимых для ее выполнения параметров следует задать символ ">", после которого ввести имя файла. В этом случае на экран монитора ничего выводиться не будет, а следовательно и ничего не будет возвращаться в псевдолитерал, т. е. после выполнения такой команды псевдолитерал будет содержать неопределенное значение (пример 6.3).
#! peri -w - $/ = "<КАТАЛОГ>";
$list = 'dir >file.dat~; # Вывод осуществляется в файл file.dat print $list; # Оператор ничего не напечатает!
Замечание
Обобщенная форма операции заключения в обратные кавычки qx {} работает точно так же, как и операция заключения в обратные кавычки " * '".
Напишите программу, которая копирует один
Упражнения
Напишите программу, которая копирует один файл в другой. Имена файлов передаются в программу при ее запуске как параметры командной строки. (Подсказка: используйте системную команду сору.)
Напишите программу, которая отображает на экране содержимое файлов, имена которых задаются в командной строке. Отображение содержимого каждого файла должно предваряться строкой, содержащей имя файла. (Подсказка: использовать операцию <>.)
Напишите программу Perl, которая удаляет файлы определенного каталога. Имена файлов задаются шаблоном, который вместе с именем каталога передается в программу через командную строку при ее запуске.
Каким образом можно получить результаты
Вопросы для самоконтроля
Каким образом можно получить результаты выполнения команды операционной системы в программе Perl?
Какая операция позволяет прочитать содержимое всех файлов, переданных сценарию Perl через командную строку?
Где хранятся имена параметров, переданных сценарию Perl через командную строку?
Можно ли получить в программе Perl имена файлов определенного каталога, удовлетворяющих заданному шаблону?
Какая списковая операция осуществляет вывод на экран монитора?
Какая списковая операция осуществляет вывод во внешний файл?
Дескрипторы файлов
Дескрипторы файлов
Когда в программе мы создаем переменные и храним в них разнообразные данные, мы теряем их по завершении работы программы. Если нам необходимо сохранить данные и использовать их в разрабатываемых программах, мы создаем файл, записываем в него данные и сохраняем его на диске. Практически любой язык программирования предоставляет программисту средства манипулирования файлами и хранимыми в них данными.
Доступ к файлам в программе Perl осуществляется через специально создаваемые дескрипторы, которые можно рассматривать как некоторый особый вид переменных. Один дескриптор в каждый момент времени может быть связан с одним и только одним файлом, хотя на протяжении всей программы один и тот же дескриптор можно последовательно связывать с разными файлами.
Более того, дескриптор можно связать не только с файлом, но и с программным каналом, обеспечивающим связь между процессами. В этой главе мы не будем касаться вопросов взаимодействия программ с другими процессами, а рассмотрим только работу с файлами и их содержимым. Поэтому дескрипторы мы иногда будем называть дескрипторами файлов.
Дескриптор — это символическое имя, которое используется в программе Perl для представления файла, устройства, сокета или программного канала. При создании дескриптора он "присоединяется" к соответствующему объекту данных и представляет его в операциях ввода/вывода. Мы дали наиболее полное определение дескриптора, чтобы читатель понимал, что дескриптор позволяет работать не только с данными файлов, но и с данными других специальных программных объектов, реализующих специфические задачи получения и передачи данных. Когда дескриптор присоединен к файлу, мы будем называть его дескриптором файла.
Замечание
При открытии файла в системе UNIX ему также назначается файловый деск-, риптор, или дескриптор файла, который ничего общего не имеет с файловым дескриптором Perl. В UNIX дескриптор файла является целым числом, тогда как в Perl это символическое имя, по которому мы можем ссылаться на файл. Чтобы получить числовой файловый дескриптор в программе Perl, можно воспользоваться функцией f ileno ().
В программе дескриптор файла чаще всего создается при открытии файла функцией open (), которой передаются два параметра — имя дескриптора и строка с именем файла и режимом доступа:
open( LOGFILE, "> /temp/logfile.log");
Этот оператор создает дескриптор с именем LOGFILE и присоединяет его к файлу с указанным именем, который открывается в режиме записи (строка второго параметра начинается с символа ">"). В этом разделе мы не будем касаться вопросов, связанных с режимом открытия файла, а сконцентрируем наше внимание на дескрипторах. В следующем разделе режимы открытия файла будут рассмотрены нами подробнее.
Дескриптор, как указывалось, является символическим именем файла и представляет собой правильный идентификатор, который не может совпадать с зарезервированными словами Perl. В нашем примере создается дескриптор LOGFILE, "замещающий" в операциях ввода/вывода файл, к которому он присоединен (/temp/logfile.log). Например, известной нам функцией print о мы можем теперь записать в этот файл значение какой-либо переменной:
print LOGFILE $var;
Любой созданный дескриптор попадает в символьную таблицу имен Perl, в которой находятся также имена всех переменных и функций. Однако дескриптор не является переменной, хотя некоторые авторы и называют его файловой переменной. В имени дескриптора не содержится никакого префикса, присущего переменным Perl ($, @ или %). Поэтому его нельзя непосредственно использовать в операции присваивания и сохранить в переменной или передать в качестве параметра в функцию. Для подобных целей приходится использовать перед его именем префикс *, который дает ссылку на глобальный тип данных. Например, предыдущий оператор печати в файл, определенный дескриптором LOGFILE, можно осуществить с помощью следующих операторов, предварительно сохранив ссылку на дескриптор в переменной $iogf:
$logf = *LOGFILE; print $logf $var;
В операции print первая переменная $iogf замещает дескриптор файла LOGFILE, в который выводится значение второй переменной $var.
( Ссылки на глобальные имена более подробно рассматриваются в )
Замечание
В программах Perl принято в именах дескрипторов использовать прописные буквы. Подобная практика позволяет легко обнаруживать их в программе и не приводит к конфликтам с зарезервированными именами функций, которые обычно определены строчными буквами.
В любой программе Perl всегда существуют три предопределенные дескриптора (STDIN, STDOUT и STDERR), которые связаны со стандартными устройствами ввода/вывода и используются некоторыми функциями Perl в качестве умалчиваемых дескрипторов файлов ввода или вывода. Как мы уже знаем, дескриптор STDIN связан со стандартным устройством ввода (обычно клавиатура), STDOUT и STDERR — со стандартным устройством вывода (обычно экран монитора). Стандартное устройство ввода используется операцией о, если в командной строке вызова сценария Perl не задан список файлов. Дескриптор STDOUT ПО уМОЛЧаНИЮ ИСПОЛЬЗуеТСЯ ФУНКЦИЯМИ print И die, а
STDERR — функцией warn. Другие функции также используют предопределенные дескрипторы файлов для вывода своей информации.
При вызове программ в среде Unix и DOS можно перенаправлять стандартный ввод и вывод в другие файлы, задавая в командной строке их имена с префиксами > для файла вывода и < для файла ввода:
peri program.pl <in.dat >out.dat
При выполнении программы program.pi все исходные данные должны быть подготовлены в файле in.dat. Вывод будет сохранен в файле out.dat, а не отображаться на экране монитора.
Перенаправление стандартного ввода и вывода, а также стандартного отображения ошибок, можно осуществлять непосредственно в программе Perl. Для этого следует функцией ореп() связать соответствующий предопределенный дескриптор с некоторым дисковым файлом:
open(STDIN, "in.dat"); open(STDOUT, ">out.dat"); open(STDERR, ">err.dat");
Теперь весь стандартный ввод/вывод будет осуществляться через указанные в операторах open о файлы. Обратите внимание, что при переопределении стандартных файлов вывода и ошибок перед именами файлов стоит префикс ">", указывающий на то, что файлы открываются в режиме записи.
Замечание
Перенаправление стандартного ввода/вывода в программе можно производить только один раз. Это переназначение действует с момента перенаправления ввода/вывода и до конца программы, причем функцией open () нельзя вернуть первоначальные установки для дескрипторов STDIN, STDOUT и STDERR.
Доступ к файлам
Доступ к файлам
Как мы уже знаем, для доступа к файлу из программы 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 $!;
При работе с не буферизованными функциями ввода/вывода следует всегда проверять завершение операции чтения, записи или позиционирования. Стандартная система ввода/вывода, через которую реализуется буферизованный ввод/вывод, сама проверяет и отвечает за завершение указанных операций, если процесс был прерван на середине записи. При не буферизованном вводе/выводе об этом должен позаботиться программист.
Совет
При работе с одним и тем же файлом не следует смешивать вызовы буферизованных и не буферизованных функций ввода/вывода. Подобная практика может приводить к непредсказуемым коллизиям.
Операции с файлами
Операции с файлами
Перед изучением функций, выполняющих действия с целыми файлами, мы напомним читателю основные положения, связанные с организацией файловой системы UNIX и процедур доступа к файлам. Функции Perl разрабатывались для работы именно с этой файловой системой, хотя в определенной степени многое из того, о чем пойдет речь, применимо и к файловым системам других платформ.
Работа пользователя в UNIX начинается с процедуры регистрации в системе, во время которой он вводит свое регистрационное имя и пароль. Регистрационное имя назначается администратором системы и хранится в специальном учетном файле. Пароль задает сам пользователь.
Регистрационное имя легко запоминается пользователем, но для системы удобнее вести учет пользователей, идентифицируя их не по символическим регистрационным именам, а по числовым идентификаторам. Поэтому каждому пользователю системы UNIX помимо мнемонического регистрационного имени присваивается также числовой идентификатор пользователя (uid — User IDentifier) и идентификатор группы (gid — Group IDentifier), к которой он относится. Значения uid и gid приписываются процессу, в котором выполняется командный интерпретатор shell, запускаемый при входе пользователя в систему. Эти же идентификаторы передаются и любому другому процессу, запускаемому пользователем во время его сеанса работы в UNIX.
Файловая система UNIX представляет собой дерево, промежуточные вершины которого соответствуют каталогам, а листья файлам или пустым каталогам. Каждый файл идентифицируется своим уникальным полным именем, которое включает в себя полный путь (pathname) от корня файловой системы через промежуточные вершины (каталоги) непосредственно к файлу. Корневой каталог имеет предопределенное имя, представляемое символом "/". Этот же символ используется и для разделения имен каталогов в цепочке полного имени файла, например /bin/prog.exe.
Каждый файл в файловой системе UNIX характеризуется значительно большим объемом информации, чем, например, файл в файловой системе FAT. Эта информация включает, в частности, данные о владельце файла, группе, к которой принадлежит владелец файла, о том, кто имеет право на чтение файла, запись в файл, на выполнение файла и т. д. Эта информация позволяет задавать разные права доступа к файлу для следующих категорий пользователей: владелец файла, члены группы владельца, прочие пользователи. Вся существенная информация о файле хранится в специальной структуре данных, называемой индексным дескриптором (mode). Индексные дескрипторы размещаются в специальной области диска, формируемой при его форматировании в системе UNIX.
При запуске процесса с ним связываются два идентификатора пользователя: действительный (real) и эффективный (effective) и два аналогичных идентификатора группы пользователей. Действительные идентификаторы пользователя и группы — это постоянные идентификаторы, связываемые со всеми процессами, запускаемыми пользователем. Эффективные идентификаторы — это временные идентификаторы, которые могут устанавливаться для выполнения определенных действий. Например, при изменении пользователем пароля программа passwd автоматически устанавливает эффективные идентификаторы процесса таким образом, чтобы обеспечить права записи в файл паролей.
Как только с процессом связаны соответствующие идентификаторы, для него начинают действовать ограничения доступа к файлам. Процесс может получить доступ к файлу только в случае, если это позволяют хранящиеся при файле ограничения доступа.
Для каждого зарегистрированного пользователя системы создается так называемый "домашний" (home) каталог пользователя, к которому он имеет неограниченный доступ, а также и ко всем каталогам и файлам, содержащимся в нем. Пользователь может создавать, удалять и модифицировать каталоги и файлы из своего домашнего каталога. Потенциально возможен доступ и ко всем другим файлам, однако он может быть ограничен, если пользователь не имеет достаточных привилегий.
Любой пользователь, создавший собственный файл, считается его владельцем. Изменить владельца файла из сценария Perl можно функцией chown (). Параметром этой функции является список, первые два элемента которого должны представлять новые .числовые идентификаторы uid и gid. Остальные элементы списка являются именами файлов, для которых изменяется владелец. Эта функция возвращает количество файлов, для которых операция изменения владельца и группы прошла успешно.
@list = ( 234, 3, "filel.dat", "file2.dat");
$number = chown(@list);
warn "Изменился владелец не у всех файлов!" if $number != @list-2;
Замечание |
|
Изменить владельца файла может только сам владелец или суперпользователь (обычно системный администратор) системы UNIX. В операционных системах с файловой системой отличной от UNIX (DOS, Windows) эта функция отрабатывает, но ее установки не влияют на доступ к файлу. |
Функция chmodo изменяет права доступа для файлов, представленных в списке, передаваемом ей в качестве параметра. Первым элементом этого списка должно быть трехзначное восьмеричное число, задающее права доступа для владельца, пользователей из группы, в которую входит владелец, и прочих пользователей. Каждая восьмеричная цифра определяет право на чтение файла, запись в файл и его выполнение (в случае если файл представляет выполняемую программу) для указанных выше групп пользователей. Установленные биты ее двоичного представления отражают соответствующие права доступа к файлу. Например, если установлены все три бита (восьмеричное число 7), то соответствующая группа пользователей обладает всеми перечисленными правами: может читать из файла, записывать в файл и выполнять его. Значение равное 6 определяет право на чтение и запись, 5 позволяет читать из файла, выполнять его, но не позволяет записывать в этот файл и т. д. Обычно не выполняемый файл создается с режимом доступа 0666 — все пользователи могут читать и записывать информацию в файл, выполняемый файл — с режимом 0777. Если владелец файла желает ограничить запись в файл пользователей не его группы, то следует выполнить следующий оператор:
chmod 0664, "file.dat";
Возвращаемым значением функции chmodo, как и функции chowno, является количество файлов из списка, для которых операция изменения прав доступа завершилась успешно.
Замечание |
|
В операционных системах DOS и Windows имеет значение только установка режимов доступа владельца. |
gfiles = ("filel.dat", "file2.dat");
$now = time;
utime $now, $now, 6files;
В этом фрагменте кода время последнего доступа и модификации файлов из списка @files изменяется на текущее время, полученное с помощью функции time.
Отметим, что при выполнении функции utime о изменяется и время последней модификации индексного дескриптора (ctime) — оно устанавливается равным текущему времени. Возвращаемым значением является количе- . ство файлов, для которых операция изменения времени последнего доступа и модификации прошла успешно.
Файловая система UNIX позволяет создавать ссылки на один и тот же файл. Это реализуется простым указанием одного и того же индексного дескриптора для двух элементов каталога. Такие ссылки называются жесткими (hard) ссылками, и операционная система не различает элемент каталога, созданный при создании файла, и ссылок на этот файл. При обращении к файлу по ссылке и по имени изменяются поля индексного дескриптора. Физически файл уничтожается только тогда, когда уничтожается последняя жесткая ссылка на файл.
В UNIX существует еще один тип ссылок на файл — символические ссылки. Эти ссылки отличаются от жестких тем, что они косвенно ссылаются на файл, имя которого хранится в блоке данных символической ссылки.
Жесткие ссылки создаются в Perl функцией linko, а символические — функцией symiinko. Синтаксис этих функций одинаков — их два параметра представляют имя файла, для которого создается ссылка, и новое имя файла-ссылки:
link СТАРЫЙ_ФАЙЛ, НОВЫЙ_ФАЙЛ; symlink СТАРЫЙ_ФАЙД, НОВЫЙ_ФАЙЛ;
При успешном создании жесткой ссылки функция link о возвращает Истина, иначе Ложь. Создание символической ссылки функцией symiinko сопровождается возвратом ею числа i в случае успешного выполнения операции иов противном случае.
Замечание |
|
В версиях Peri для DOS эти функции не реализованы, и при попытке их вызова интерпретатор выдает фатальную ошибку: |
The symlink function is unimplemented at D:\EX2.PL line 2.
Удалить существующие ссылки на файл можно функцией unlink о. Эта функция удаляет одну ссылку на каждый файл, заданный в списке ее параметров. Если ссылок на файл не существует, то удаляется сам файл. Функция возвращает количество файлов, для которых успешно прошла операция удаления. Вызов функции unlink без списка параметров использует содержимое специальной переменной $_ в качестве списка параметров. Следующий фрагмент кода удаляет все резервные копии файлов текущего каталога:
unlink <*.bak>;
В структуре индексного дескриптора поле nlink содержит количество жестких ссылок на файл. Его можно использовать совместно с функцией unlink о для удаления всех ссылок на файл. Если ссылок нет, то это поле имеет значение 1 (только имя файла, определенное при его создании, ссылается на индексный дескриптор файла).
Замечание |
|
Каталоги в UNIX являются файлами специального вида. Однако их нельзя удалить функцией unlink, если только вы не суперпользователь или при запуске peri не используется флаг -и. Для удаления каталогов рекомендуется использовать функцию rmdir (). |
rename "old.dat", "new.dat";
Этот оператор переименует файл old.dat в файл new.dat. Функция переименования файла возвращает i при успешном выполнении этой операции и о в противном случае.
Функция truncated усекает файл до заданной длины. Для задания файла можно использовать как имя файла, так и дескриптор открытого файла:
truncate ДЕСКРИПТОР, ДЛИНА; truncate ИМЯ_ФАЙЛА, ДЛИНА;
Функция возвращает значение Истина, если длина файла успешно усечена до количества байт, определенных в параметре ДЛИНА, или неопределенное значение undef в противном случае. Под усечением файла понимается не только уменьшение его длины, но и увеличение. Это означает, что значение второго параметра функции truncate о может быть больше истинной длины файла, что позволяет делать "дыры" в содержимом файла, которые в дальнейшем можно использовать для записи необходимой информации, не уничтожая уже записанную в файл (пример 7.7).
#! peri -w
# Создание файла с "дырами" for($i=l;$i<=3;$i++H
open(F, "»out.dat") or die $!; print F "Запись".$i;
close F;
open(F, "»out.dat") or die $!; truncate F, 19*$i;
close F;
} ' tt Запись информации в "дыры" open(F, "+<out.dat") or die $!; for($i=l;$i<=3;$i++){
seek F, 0,1;
read F,$reel,7;
seek F,0,l;
print F "<CONTENTS:".$i.">"; } close F;
На каждом шаге первого цикла for примера 7.7 в конец файла out.dat записывается информация длиной 7 байтов, а потом его длина увеличивается на 12 байтов, образуя пустое пространство в файле. Следующий цикл for заносит в эти созданные "дыры" информацию длиной 12 байтов, не затирая хранящуюся в файле информацию. Обратите внимание, что для изменения длины файла функцией truncate приходится закрывать его и снова открывать. Это связано с тем обстоятельством, что функция truncate о добавляет пустое пространство в начало файла, сдвигая в конец его содержимое, если применять ее, не закрывая файл. Можете поэкспериментировать с программой примера 7.7, открыв файл перед выполнением первого цикла for, и закрыв его после завершения цикла. Содержимое файла даст вам наглядное представление о работе функции truncate в этом случае. У нас же после выполнения первого цикла for содержимое файла out.dat выглядит так:
Запись1 Запись2 Запись3
\
По завершении всей программы файл будет содержать следующую строку:
Запись<CONTENTS:1>Запись2<CONTENTS:2>ЗаписьЗ<CONTENTS:3>
Операции с каталогами
Операции с каталогами
Как мы отмечали ранее, в UNIX каталоги являются файлами специального формата, помеченными в структурах своих индексных дескрипторов как каталоги (поле rdev). Содержимым блоков данных каталогов является множество пар, состоящих из объекта, содержащегося в каталоге, и числового значения его индексного дескриптора.
Для работы с каталогами в Perl предусмотрены функции открытия, закрытия и чтения содержимого каталога, синтаксис и семантика которых аналогичны синтаксису и семантики соответствующих операций с файлами:
opendir ДЕСКРИПТОР, ИМЯ_КАТАЛОГА; closedir ДЕСКРИПТОР; readdir ДЕСКРИПТОР;
Доступ к содержимому каталога осуществляется, как и в случае с файлом, через создаваемый функцией opendir о дескриптор каталога. Отметим, что для дескрипторов каталогов в таблице символов Perl создается собственное пространство имен. Это означает, что в программе могут существовать, совершенно не конфликтуя между собой, дескрипторы файла и каталога с одинаковыми именами:
open FF, "/usr/out.dat" # Дескриптор файла opendir FF, Vusr" # Дескриптор каталога
Замечание
Использование одинаковых имен для дескрипторов файла и каталога может запутать самого пользователя. Однако для peri такой проблемы не существует: интерпретатор всегда точно знает, какой дескриптор следует использовать.
Функция readdir о для открытого каталога в списковом контексте возвращает список имен всех файлов каталога или пустой список, если все имена уже были прочитаны. Эта же функция в скалярном контексте возвращает следующее имя файла каталога или неопределенное значение undef, если были прочитаны все имена файлов.
Функцией rewinddiro текущая позиция в каталоге устанавливается на начало, что позволяет осуществлять повторное чтение имен файлов каталога, не закрывая его. Единственным параметром этой функции является дескриптор открытого каталога.
Программа примера 7.8 проверяет, являются все файлы каталога двоичными (содержимое вложенных каталогов не проверяется).
#! peri -w
opendir FDIR, "/usr/prog"; while( $name = readdir FDIR) { next if -d $name; # Каталог
print("$name: двоичный\п") if -B $name; tt Двоичный файл } closedir FDIR;
Функция readdir о возвращает относительное имя файла. Для получения полного имени файла следует создать его в программе самостоятельно. Например, добавить имя проверяемого каталога в примере 7.8:
print("/usr/prog/$name: двоичныйХп") if -В $name; # Двоичный файл
Для создания нового каталога следует воспользоваться функцией mkdiro, параметрами которой являются имя каталога и режим доступа (восьмеричное число):
mkdir ИМЯ_КАТАЛОГА, РЕЖИМ;
Если задается не полное имя каталога, то он создается в текущем каталоге, устанавливаемом функцией chdirf). Возвращаемым значением, функции создания нового каталога mkdir о является Истина, если каталог создан, и Ложь, если произошла какая-то ошибка. В последнем случае в специальной переменной $! хранится объяснение не выполнения операции создания каталога.
Совет |
|
Для каталогов рекомендуется задавать режим доступа равным 0777. |
Замечание |
|
Функция rmdir () удаляет только пустой" каталог. Если он содержит другие пустые каталоги, их надо удалить ранее. |
Получение информации о файле
Получение информации о файле
Мы знаем, что в файловой системе UNIX информация о файле хранится в его индексном дескрипторе (inode). Структура индексного дескриптора состоит из 13 полей, для которых используются специальные обозначения. Все они перечислены в табл. 7.2.
Таблица 7.2. Структура индексного дескриптора
Поле | Описание | |
dev | Номер устройства в файловой системе | |
ino | Номер индексного дескриптора | |
mode | Режим файла (тип и права доступа) | |
nlink | Количество жестких ссылок на файл (в отсутствии ссылок равно 1) | |
uid | Числовой идентификатор владельца файла | |
gid | Числовой идентификатор группы владельца файла | |
rdev | Идентификатор устройства (только для специальных файлов) | |
size | Размер файла в байтах | |
a time | Время последнего обращения к файлу с начала эпохи | |
mtime | Время последнего изменения файла с начала эпохи | |
с time | Время изменения индексного дескриптора с начала эпохи | |
blksize | Предпочтительный размер блока для операций ввода/вывода | |
blocks | Фактическое количество выделенных блоков для размещения файла |
Замечание | ||
Начало эпохи датируется 1 января 1970 года 0 часов 0 минут. |
Замечание | ||
Не все перечисленные в табл. 7.2 поля структуры индексного дескриптора поддерживаются всеми файловыми системами. |
Для получения значений полей структуры индексного дескриптора файла в Perl предназначена функция stato. Ее единственным параметром может быть либо имя файла, либо дескриптор открытого в программе файла. Она возвращает список из 13 элементов, содержащих значения полей структуры индексного дескриптора файла в том порядке, как они перечислены в табл. 7.2. Типичное использование в программе Perl представлено ниже
($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size, $atime,$mtime,$ctime,$blksize,$blocks) = stat($filename);
Присваивание значений полей списку скалярных переменных с идентификаторами, соответствующими названиям полей, способствует лучшей читаемости программы, чем присваивание массиву скаляров:
@inode = stat($filename);
В последнем случае получить значение соответствующего поля можно только с помощью индекса, что не совсем удобно, так как надо помнить номер нужного поля структуры.
Если при обращении к функции stato не указан параметр, то она возвращает структуру индексного дескриптора файла, чье имя содержится в специальной переменной $_.
Функция получения информации о файле при успешном выполнении в списковом контексте возвращает список значений полей структуры индексного дескриптора файла или пустой список в случае неудачного завершения. В скалярном контексте она возвращает булево значение Истина или Ложь в зависимости от результатов своего выполнения.
Для удобства использования информации о файле функция stato при успешном выполнении кэширует полученные значения полей. Если вызвать эту функцию со специальным дескриптором файла _ (символ подчеркивания), то она возвратит информацию, хранящуюся в кэше от предыдущего ее вызова. Это позволяет проверять различные атрибуты файла без повторного вызова функции stat () или сохранения результатов ее выполнения в переменных программы.
Функцию stato можно использовать для получения структуры индексного дескриптора не только файла, но и жестких ссылок на него, а также каталогов, так как они являются также файлами, блоки данных которых содержат имена файлов каталога и их числовых индексных дескрипторов. Для получения информации о символических ссылках следует использовать функцию is tat о, которая возвращает список значений полей структуры индексного дескриптора самой ссылки, а не файла, на который она ссылается. Эта функция работает аналогично функции stat (), включая использование специального дескриптора _.
Замечание |
|
Если операционная система не поддерживает символические ссылки, то обращение к функции Istat {} заменяется обращением к функции stat (). |
Таблица 7.3. Унарные именованные операции проверки файлов
Операция |
Проверяемый атрибут |
-r |
Файл может читаться эффективным uid/gid |
-W |
Записывать в файл может эффективный uid/gid |
-х |
Файл может выполняться эффективным uid/gid |
-о |
Владельцем файла является эффективный uid |
-R |
Файл может читаться действительным uid/gid |
-W |
Записывать в файл может действительный uid/gid |
-X |
Файл может выполняться действительный uid/gid |
-0 |
Владельцем файла является действительный uid |
-е |
Файл существует |
-Z |
Размер файла равен нулю |
-S |
Размер файла отличен от нуля (возвращается размер) |
-f |
Файл является обычным (plain) файлом |
-d |
Файл является каталогом |
-1 |
Файл является символической ссылкой |
-Р |
Файл является именованным программным каналом (FIFO) или проверяемый дескриптор связан с программным каналом |
-S |
Файл является сокетом |
-b |
Файл является специальным блочным файлом |
™ С |
Файл является специальным символьным файлом |
-t |
Дескриптор файла связан с терминалом |
-и |
У файла установлен бит setuid |
-g |
У файла установлен бит setgid |
-k |
У файла установлен бит запрета (sticky bit) |
-k |
У файла установлен бит запрета (sticky bit) |
-т |
Файл является текстовым файлом. |
-b |
Файл является двоичным (противоположным текстовому) |
-м |
Возраст файла в днях на момент выполнения программы |
-А |
То же для времени последнего обращения к файлу |
-С |
То же для времени последней модификации индексного дескриптора файла |
Несколько слов об алгоритме определения текстовых и двоичных файлов (операции -т и -в). Эти операции анализируют содержимое первого блока файла на наличие "странных" символов — необычных управляющих последовательностей или байтов с установленными старшими битами. Если обнаружено достаточно большое количество подобных символов (больше 30%), то файл считается двоичным, иначе текстовым. Любой файл с пустым первым блоком рассматривается как двоичный.
Если эти операции применяются к файловым дескрипторам Perl, то проверяется содержимое буфера ввода/вывода, а не первого блока файла. Обе эти операции, примененные к файловым дескрипторам, возвращают булево значение Истина, если ввязанный с дескриптором файл пуст или установлен на конец файла.
При выполнении унарных именованных операций проверки файла на самом деле неявно вызывается функция statо, причем результаты ее вычисления кэшируются, что позволяет использовать специальный файловый дескриптор _ для ускорения множественных проверок файла:
if( -s("filename") && -Т J {
^ Что-то делаем для текстовых файлов не нулевого размера.
}
Напишите программу, которая удаляет каталог,
Упражнения
1. Найдите ошибки в программе:
#1 peri -w
$var = (stat "filel.dat")[7];
open FILE1 ">filel.dat";
print FILE1, "Длина файла: " . $var . "\n";
2. Напишите программу, которая удаляет каталог, имя которого передается через командную строку. Если сценарий запущен без параметров, то предусмотрите отображение сообщения о правильном синтаксисе его вызова. (Указание: сначала следует удалить все файлы из нижележащих каталогов, если таковые имеются.)
3. Напишите программу копирования одного файла в другой. Предусмотрите ввод имен файлов как через командную строку, так и с экрана монитора.
4. Напишите программу копирования содержимого одного каталога в другой каталог. Предусмотрите ввод имен каталогов как через командную строку, так и с экрана монитора.
5. Напишите программу чтения строки текстового файла с заданным номером. Предусмотрите случаи, когда номер заданной строки превосходит количество строк в файле. Если номер строки отрицательный, то следует прочитать все строки, начиная со строки с номером, равным абсолютному значению введенного отрицательного значения.
к файлу из программы Perl?
Вопросы для самоконтроля
1. Как можно получить доступ к файлу из программы Perl?
2. Перечислите операции, позволяющие читать содержимое файла и записывать в него информацию:
3. Какие существуют режимы открытия файла и чем они отличаются?
4. Что такое режим доступа файла и как его можно изменить в программе Perl?
5. Что такое дескриптор каталога и зачем он нужен?
6. Как можно получить имена всех файлов определённого каталога?
Использование нескольких форматов
Использование нескольких форматов
Как мы уже знаем, форматы Perl позволяют без каких-либо усилий создавать верхние колонтитулы — следует только объявить формат с суффиксом _ТОР. Для создания полноценного документа не мешало бы еще иметь возможность создавать нижние колонтитулы страницы и печатать, например, в конце заказа общую стоимость. К сожалению, такой возможности Perl не предоставляет, но он позволяет переключать вывод с одного формата на другой и в специальной переменной хранит строку, которую печатает перед переходом на новую страницу. А это и позволит нам создать и напечатать и нижний колонтитул, и общую стоимость заказа.
Но прежде мы еще немного поговорим о специальных переменных Perl, которые используются для управления форматом. В переменной $~ хранится имя формата, который используется при выводе функцией write о без параметра:
write; # Эквивалентно оператору write STDOUT;
По умолчанию в ней хранится имя формата STDOUT, но и вывод функцией write о без параметра происходит на стандартное устройство вывода STDOUT. (Мы помним, что имя формата должно совпадать с именем дескриптора файла в вызове функции write о, а именно такая ситуация по умолчанию и реализуется.) Если мы изменим значение переменной $~ на имя другого формата, то вывод в стандартный файл функцией write о без параметра будет осуществляться в соответствии с указанным форматом, который, конечно, должен быть объявлен в программе. Например, следующий оператор write выводит на стандартное устройство вывода в соответствии с форматом NEW:
$- = NEW; write/format NEW =
Таким образом, меняя значение переменной $~, можно переключать вывод с одного формата на другой. Этим другим форматом как раз и может быть формат общей стоимости заказа.
Пусть в файле books содержится информация о заказанных книжным магазином книг. В конце отчета по заказу нам теперь необходимо напечатать общую стоимость книг. Решение показано в программе примера 8.2.
#! peri -w
open BOOKS, "<books"; # Открытие файла на чтение
$number = 1;
$total = 0;
while (<BOOKS>) {
($author, $title, $pub, $year, $price) = split(':'); # Разбиение строки
t по символу ':'
write; # Форматный вывод строки $total += $price; t Подсчет общей суммы
}
$~ = TOTAL; # Переключение формата
write; # Вывод по формату итоговой строки
format STDOUTJTOP =
Заказ № @#
$ number Автор Название Издатель Год Цена
format STDOUT = '
Л <«««««««««««« | @»»»»>» | @М!11М I @t### I @###.##р.
$author, $title, $pub, $year, $price
A «««««««««<««« | | | | ~~
$author
format TOTAL =
Итого: @###.##р. $total
В этой программе после форматной печати содержимого файла books осуществляется переключение на другой формат, по которому выводится строка с общей суммой заказа, подсчитанной в переменной $totai. Полученный с помощью этой программы заказ показан ниже
Заказ № I Автор Название Издатель Год Цена
В.Долженков Ю.Колесников I Excel 2000 I BHV | 1999 I 90.00р.
А.Матросов А.Сергеев I HTML 4.0 I BHV | 1999 | 70.00р.
М.Чаунин I I II
Т.Кристиансен Н.Торкингтон I Perl I Питер | 2000 | 100.00р.
Итого: 260.00р.
В завершение разговора о создании отчетов в Perl мы модифицируем программу примера 8.1, приспособив ее для печати отчета на основании информации о книгах из файла books, в котором в записи о книгах добавлено еще одно поле, содержащее краткую аннотацию книги:
В.Долженков Ю.Колесников:Excel 2000:BHV:1999:90:Аннотация книги
Отчет, формируемый этой программой (пример 8.3), также печатает нижний колонтитул на каждой странице. Для этого мы воспользуемся специальной переменной $ л ь, содержимое которой Perl печатает перед переходом на новую страницу во время форматного вывода. При этом следует уменьшить на количество строк, заданных в этой переменной, количество строк на странице, хранящееся в специальной переменной $=, иначе строки из переменной $ л ь попадут не в конец текущей страницы, а будут напечатаны на следующей странице, не создав никакого нижнего колонтитула.
#! peri -w
open FILE, "<books" or die $!;
open REPORT, "xreport" or die $!;
select REPORT; $~ •= STDOUT; $= = 24;
Sltime = localtime;
$-L = •("=" x 73) . "\n". "Книготорговая база \"БЕСЫ\"". (" " x 24)."$ltime\n\f";
$count = 0;
while(<FILE>) { .
($author, $title, $pub, $year, $price, $annot) = split(':');
$count++;
write ;
}
close(REPORT); format STDOUT_TOP =
Книги на складе @»»>»
; "стр. ".$%
Автор Название Издатель Год Цена Аннотация ________
format STDOUT =
@| Л «««««« /ч «««««« @<««« @### @##.##р. Л «««««««««
$count.".", $author, $title, $pub, $year, $price, $annot
Л «««««« л «««««« А «««««««««
$author, $title, $annot
Вывод отчета осуществляется в файл с именем report. Обратите внимание на задание переменной $ /4 L. В ней используется переменная $itime, в которой хранится текущая дата, полученная обращением к функции localtime. Одна страница отчета будет выглядеть следующим образом:
Книги на складе . стр. t Автор _______ Название ____ Издатель Год Цена Аннотация ________
1. В.Долженков Excel 2000 BHV 1999 90.00р. Книга является
Ю.Колесников справочным пособием
по MS Excel 2000. В ней рассматриваются следующие основные темы - настройка интерфейса и его основные элементы.
2. А.Матросов HTML 4.0 BHV 1999 70.00р. Представлен весь А.Сергеев спектр технологий М.Чаунин создания Web-документов (начиная от простейших -
статических - и до документов на основе
динамического HTML), включая форматирование текста, создание списков. Книготорговая база "БЕСЫ" Sat Mar 18 19:01:37 2000
Замечание |
|
Представленные в этой главе отчеты являются снятыми копиями экрана монитора, вывод на который осуществляется с использованием моноширинного шрифта. Если вывод осуществляется на принтер, то чтобы отчеты выглядели так, как они должны выглядеть, следует также использовать моноширинный шрифт, например Courier. Если используется пропорциональный шрифт, принятый на многих принтерах по умолчанию, то сформированные сценарием Perl отчеты "поползут", так как в в этих шрифтах каждый символ имеет собственную ширину, тогда как в моноширинных все символы имеют одинаковую ширину. |
Объявление формата
Объявление формата
Как мы помним, дословный перевод аббревиатуры языка Perl включает в себя слова "язык отчетов", т. е. язык Perl предназначен не только для извлечения и обработки информации из текстовых файлов, но и для генерирования отчетов на основе этой информации. Пока что мы для вывода информации использовали функцию print (), которая не очень-то удобна для создания отчетов — определенным образом отформатированной выходной информации. (Можно было бы воспользоваться функцией форматированного вывода printf о, но мы решили не нагружать нашего читателя изучением языковых средств, которыми он редко будет пользоваться, тем более что всегда можно обратиться к документации Perl.)
Для создания простых отчетов в Perl предусмотрены форматы, которые позволяют в тексте программы практически визуализировать внешний вид выводимого отчета, так как определение формата в Perl очень близко к тому, что отображается при выводе. Форматы позволяют задавать верхний колонтитул каждой страницы отчета, куда можно поместить название документа, номер страницы и другую полезную информацию. Perl может отслеживать количество строк на странице отчета и автоматически переходить на новую страницу по заполнению всех строк предыдущей.
Использование форматов для создания отчетов очень просто. Первое, что необходимо сделать, — определить формат и переменные, которые в нем используются. Далее следует инициализировать в программе эти переменные и осуществить форматированный вывод в файл, определенный своим дескриптором, с помощью функции write ().
Эти и другие, связанные с форматами, вопросы и являются предметом изучения в этой главе. Начнем мы с основного вопроса — объявление форматов в программе Perl.
Формат — это одна из двух языковых единиц (вторая — подпрограмма sub), которая требует обязательного объявления в программе Perl. Он используется в качестве "руководства" функцией write о, которая выводит на экран монитора, принтер или в файл информацию из программы в соответствии с записанными в формате "инструкциями" форматирования строк вывода. При объявлении формата определяется, как должна быть отформатирована каждая его строка при отображении на устройстве вывода.
Формат объявляется в программе с помощью ключевого слова format, после которого следуют "инструкции" по форматированию определенных в нем строк. Завершается объявление формата строкой, первым символом которой является точка ".". Общий синтаксис конструкции объявления формата следующий: /
format ИМЯ_ФОРМАТА •= ФОРМАТЫ_СТРОК
Параметр ИМЯ_ФОРМАТА представляет собой правильный идентификатор Perl. Он должен в точности соответствовать имени дескриптора файла, который используется в качестве единственного параметра в функции вывода write о. Например, если форматированный отчет выводится в файл, определенный в программе дескриптором FILE, то и имя формата должно быть также FILE. Функцию write о можно вызывать без параметра. В этом случае вывод осуществляется на стандартное устройство вывода (STDOUT), и имя формата в этом случае должно быть равным STDOUT. Если функцией select о установлен дескриптор файла вывода по умолчанию, то вывод функцией write о без параметра будет осуществляться в этот файл, причем имя формата вывода должно быть изменено на имя дескриптора файла.
Замечание |
|
Так как операция format не является вычисляемой операцией Perl, то объявление формата может осуществляться в любом месте программы. Обычно все объявления форматов задают либо в начале, либо в конце программы. |
Замечание |
|
Имя формата, как отмечалось, может быть любым правильным идентификатором Perl, однако обычно его определяют прописными буквами, что способствует лучшей читаемости программы. |
Строка шаблонов печатается точно так, как она выглядит в тексте программы (включая пробельные символы), за исключением некоторых полей, в которые подставляются значения переменных из строки переменных. Эти поля (иногда их называют шаблоны, что и дало название соответствующей строке формата) начинаются с символа "в" или " ^ ", за которым следуют символы форматирования (табл. 8.1), определяющие ширину поля вывода значения переменной в символах и выравнивание выводимого значения внутри поля. Количество символов форматирования определяет ширину поля вывода, причем для одного поля все символы должны быть одинакового типа.
Переменные, определяющие значения для полей строки шаблонов, задаются через запятую в строке переменных. Порядок их задания соответствует порядку задания полей вывода в строке шаблонов: значение первой переменной выводится в первое поле, второй — во второе и т. д. Все переменные в строке переменных вычисляются в списковом контексте. Это позволяет задавать выводимые значения в элементах массива скаляров.
Замечание |
|
Если строка шаблонов не содержит полей, то для нее не надо задавать строку переменных. Она отображается в точности так, как она задана в формате. |
Символ |
Описание |
> |
Определяет символьное поле, в котором выводимое значение выровнено по правому краю |
< |
Определяет символьное поле, в котором выводимое значение выровнено по левому краю |
# | Определяет числовое поле (выводимое значение должно быть числом) |
. | Определяет положение десятичной точки в числовом поле (###.##) |
I |
Определяет символьное поле, в котором выводимое значение выровнено центру |
В.Долженков Ю.Колесников:Excel 2000:BHV:1999:90
Нам необходимо распечатать отчет о всех продаваемых книгах. Воспользуемся форматами Perl. Программа примера 8.1 реализует поставленную задачу.
#! peri -w . "
open BOOKS, "<books"; # Открытие файла на чтение
while (<BOOKS>) {
{$author, $title, $pub, $year, $price) = split(':'); t Разбиение строки по символу ':'
write; # Форматный вывод строки
} '.....
format STDOUT =
@<«««««««««««« | е»»»»>» i @i 111111 | @#### | @###.##р.
$author, $title, $pub, $year, $price
Результат отображения отчета на экране монитора выглядит следующим образом:
В.Долженков Ю.Колесников |, Excel 2000 | BHV | 1999 | 90.00р.
А.Матросов А.Сергеев М.Чау | HTML 4.0 I BHV | 1999 | 70.00р.
Т.Кристиансен Н.Торкингтон | Perl | Питер | 2000 | 100.00р.
Обратите внимание, что все символы строки шаблонов печатаются именно в тех позициях, в которых они заданы, а в поля этой же строки, определенные символом "@", подставлены значения соответствующих переменных. Эти значения отображаются в соответствии с заданными символами форматирования: для переменной $authqr вывод выровнен по левому краю (<), для $ title по правому краю (>), для $pub пр центру (|) соответствующего поля. Значения переменных $уеаг и $price форматируются в соответствии с числовым форматом. Если бы эти переменные не содержали числовые значения, то интерпретатор peri вывел бы предупреждающее сообщение.
Замечание |
|
Символ начала поля в строке шаблонов ("@" или " ^ ") учитывается при подсчете ширины поля вывода. |
Как поступать в таких случаях? Можно увеличить ширину поля, если позволяют параметры выводного устройства, а можно воспользоваться еще одним символом форматирования, который как раз и предназначен для решения подобных проблем. Прежде всего следует для задания начала поля использовать символ " ^ ". Его отличие от символа "е" заключается в том, что до начала вывода строковых данных в поле Perl в промежуточном буфере аккумулирует слова из выводимых данных, формируя строку, длина которой не превышает ширину поля. После этого сформированные данные выводятся в строке, а значение переменной вывода модифицируется: она будет содержать оставшиеся слова. При последующем использовании этой переменной в другой строке переменных того же формата будут выводиться сохраненные в ней отсеченные данные. Это позволяет выводить длинные данные в нескольких строках в одном вертикальном блоке, если задавать для вывода оставшихся данных точно такое же поле, что и при выводе первой порции.
Заменим формат STDOUT программы примера 8.1 на следующий:
format STDOUT =
-<«««««««««««« | @»»»»>» | @| | @##it f @###.##р.
$author, $title, $pub, $year, $price
Л ««««««««««<«« | | I | ~
$author
Теперь вывод нашей программы будет выглядеть так:
В.Долженков Ю.Колесников I Excel 2000 | BHV | 1999 | 90.00р.
А.Матросов А.Сергеев | ' HTML 4.О Г BHV | 1999 | 70.00р.
М.Чаунин I I II
Т.Кристиансен Н.Торкингтон I Perl | Питер | 20.00 | 100.00р.
Символ тильда "~" в конце строки Шаблона подавляет вывод пустых строк. Если не поставить его, то между первой и второй книгой в нашем отчете появится дополнительная строка, как если бы была выведена вторая строка шаблона с пустым значением переменной $author. Символ подавления вывода пустых строк можно задавать в любом месте строки шаблона, помня, что при выводе он отображается, как пробел.
В нашем примере мы знали, что данные в переменной $author He займут более двух строк при выводе. Поэтому в формате мы использовали эту информацию, добавив еще одну строку шаблона с переменной $author. А что делать, если не известно количество строк продолжения в которых будут выводиться данные? Можно воспользоваться двумя идущими подряд символами тильда вместо одного. В этщ случае алгоритм буферизации данных по словам будет продолжаться до завершения вывода всех данных переменной. Если наш формат изменить на следующий
format STDOUT = . _....,..,... . ; ,
Л <«««««««««««« | @»»»»>» ,| @| | | | | | в#### I @###.-##р.
$author, $title, $pub, $year, $price
$author
а во вторую книгу добавить еще парочку авторов, то вывод записи об этой книге нашей программой будет иметь следующий вид:
А.Матросов А.Сергеев | HTML 4.0 I BHV | 1999 | 70.00р.
М.Чаунин В.Долженков I III
Ю.Колесников I III
Таким способом можно организовать вывод длинных данных в вертикальные текстовые блоки с переносом по словам.
Немного отвлечемся от создания отчета по книгам, чтобы познакомить читателя с еще одним символом форматирования — символом "*", который позволяет выводить длинные строковые данные в нескольких строках, длина которых равна максимальной ширине вывода устройства отображения (экрана монитора или принтера). Например, если переменная $ record содержит строковые данные длиной более 80 символов и вывод осуществляется на экран монитора, то следующий фрагмент кода Perl
write;
format STDOUT =
$*
$record
отобразит на экране ее содержимое следующим образом:
В.Долженков Ю.Колесников:Excel 2000:BHV:1999:90:Книга является справочным пособием по MS Excel 2000. В ней рассматриваются следующие основные темы - настройка интерфейса и его основные элементы.
Замечание |
|
Используемый в нашей книге шрифт для представления вывода сценария Perl не позволяет нам отобразить истинный вывод в соответствии с заданным форматом, так как внимательный читатель обнаружит, что длина строки вывода в нашем представлении составляет 73 символа, а не 80, как при выводе на экран монитора. |
Итак, мы теперь знаем, что переход на новую страницу происходит автоматически, но нам хотелось бы, чтобы на каждой странице печатался верхний колонтитул, в котором отображалось бы наименование отчета и печатались номера страниц. И это возможно в Perl. Следует только задать формат со специальным именем, добавив к имени формата, по которому мы выводим информацию (в нашей программе STDOUT), суффикс _ТОР. Этот формат будет выводиться каждый раз, как начинается печать новой страницы.
Добавим в программу примера 8.1 следующее объявление формата
format STDOUTJTOP =
Книги на складе @>»>»
"стр. ".$% Автор Название Издатель Год Цена
и явно зададим количество строк на странице, добавив перед циклом while оператор
$= = б;
Теперь наша программа напечатает две страницы отчета, причем на каждой из них будет напечатан колонтитул:
Книги на складе стр. 1 Автор - Название Издатель Год Цена
В.Долженков Ю.Колесников | Excel 2000 I BHV | 1999 | 90.00р. А.Матросов А.Сергеев | HTML 4.0 I BHV | 1999 | 70.00р. М.Чаунин ,| | II
———разрыв страницы———
Книги на складе стр. 2 Автор Название Издатель Год Цена
Т.Кристиансен Н.Торкингтон | Perl I Питер | 2000 | 100.00р.
Вернемся к объявлению формата для колонтитула. Во-первых, при его задании мы использовали выражение "стр. ".$% в строке переменных. Действительно, хотя формат и не вычисляется, но во время выполнения программы вычисляются значения переменных и все выражения строки переменных
формата. Во-вторых, мы использовали специальную переменную $%, которая хранит текущий номер выводимой страницы. Это позволило нам в колонтитуле напечатать номера страниц.
Какой синтаксис имеет оператор format?
Вопросы для самоконтроля
1. Опишите процедуру создания отчетов в Perl.
2. Какой синтаксис имеет оператор format?
3. Какая функция используется для активизации форматного вывода?
4. Как создается верхний колонтитул для страниц отчета?
5. Как создается нижний колонтитул для страниц отчета?
6. Каким образом осуществляется переключение между форматами?
7. Перечислите специальные переменные Perl, которые используются для управления форматным выводом.