54844

Perl. Специальный справочник

Книга

Педагогика и дидактика

Цель этой книги дать вам весь материал необходимый для того чтобы стать программистом на языке Perl. Perl это не просто обычный язык программирования. Perl больше чем язык для поэтов и фанатов программирования он источник творческого вдохновения и средство для его реализации. С момента начала моей работы с Perl до появления идеи создания книги о нем прошли многие годы.

Русский

2014-03-19

2.54 MB

14 чел.

Perl. Специальный справочник
С. Холзнер

Введение

Цель этой книги — дать вам весь материал, необходимый для того, чтобы стать программистом на языке Perl. И это говорит о многом. Perl — это не просто обычный язык программирования. Он возбуждает преданность, страсть, экзальтированность, эксцентричность (не говоря уже о раздражении и расстройстве). Perl — больше, чем язык, для поэтов и фанатов программирования он источник творческого вдохновения и средство для его реализации. Он может быть сложным и загадочным, вводящим в заблуждение и противоречивым. Но для истинного разработчика другого пути нет. Вы поймете, что именно я имею в виду, когда прочтете эту книгу.

На двенадцатый год своего господства Практический Язык для Извлечения текстов и Генерации отчетов (Practical Extraction and Reporting Language) — также называемый некоторыми Патологически Эклектичный Язык для Распечаток Чепухи (Pathologically Eclectic Rubbish Listing) — стал всеобщим любимцем. Потрясающее количество людей посвятило впечатляющее количество свободного времени работе с ним, его совершенствованию и повсеместному распространению. С момента начала моей работы с Perl до появления идеи создания книги о нем прошли многие годы. Возможно, увидев, как работает Perl, вы также станете его поклонником.

Что есть в этой книге

Эта книга не только объясняет синтаксис языка Perl, она также дает реалистическое представление о том, что из себя представляет Perl сегодня и как он используется. Например, когда вы заглядываете в мир Интернет, Perl буквально повсюду — поэтому в книге уделяется достаточно много внимания CGI-программированию. Также популярный сегодня вопрос о связи между Perl и Tk, который позволяет с помощью Perl выводить на экран окна, кнопки, меню и др. элементы графического интерфейса, — стал другой темой данной книги. Затрагиваются также и другие вполне практические вопросы типа подключения Perl к базам данных, серверам, работающим по технологии Windows OLE Automation, к другим процессам, и так далее. Все это вы найдете в этой книге. В целом, она написана так, чтобы дать вам наиболее полное представление о том, что происходит с языком Perl сегодня.

Кроме информации непосредственно о языке Perl версии 5, в этой книге вы также найдете множество дополнительных сведений — это и создание более читаемого Perl-кода, и блоки BEGIN и END для пакетов, совместимость с операционными системами класса POSIX, объектно-ориентированное программирование, произвольно вложенные структуры данных, лексические области видимости, расширенные возможности по использованию модулей, а также другие темы. Версия 5 стала блестящей реализацией возможностей языка Perl, поэтому книга написана именно на основе этой версии.

Книга разбита на отдельные, легко читаемые отрывки (примерно 500 тем), каждый из которых относится к определенному разделу программирования. Вот некоторые из них:

Синтаксис языка Perl версии 5: команды и описания

Интерактивный запуск Perl-сценариев

Текстовый ввод и вывод

Создание скалярных переменных

Скалярный контекст и контекст списка

Создание массивов и хешей

Циклы и условные операторы

Таблицы символов и тип данных typeglob

Операторы языка Perl

Регулярные выражения и работа со строками

Создание подпрограмм

Переменные с лексической областью видимости

Временные переменные

Устойчивые (persistent), или статические переменные

Рекурсивные подпрограммы

Анонимные массивы, хеши и подпрограммы

Ссылки в языке Perl

Символические ссылки

Устойчивые (persistent) ограничители области видимости

Шаблоны функций

Специальные переменные Perl

Встроенные функции Perl

Функции POSIX

Форматы языка Perl

Взаимодействие между процессами

Технологии Win32 OLE Automation

Работа с файлами

Файлы баз данных DBM

Блокировка файлов

Операторы Perl для работы с оглавлениями

Тесты быстродействия

Операторы, чувствительные к локальным настройкам

Безопасное изолирование кода

Perl/Tk: использование библиотеки Tk

Сложные записи

Массивы массивов, хеши хешей, массивы хешей и хеши массивов

Связанные списки и кольцевые буферы

Пакеты Perl

Конструкторы и деструкторы пакетов

Разбивка пакета на несколько файлов

Модули Perl

Экспорт символов из модулей

Автозагрузка подпрограмм модулей

Классы Perl

Конструкторы классов

Объекты Perl

Методы классов

Переменные экземпляра класса и переменные класса

Наследование классов

Множественное наследование

Связывание скаляров, массивов и хешей

Перехват ошибок времени выполнения

Отладчик Perl

Язык PerlScript

Программирование Common Gateway Interface (CGI-программирование)

Создание и использование форм HTML в CGI

Защита данных для CGI

Меченые данные и восстановление данных

Как дать повышенный приоритет и права CGI-сценарию

Создание своего Web-счетчика

Создание гостевой книги

Как послать письмо через CGI-сценарий

Общение в реальном времени (создание чат-приложений)

Вопросы секретности для многопользовательского режима

Отражение атак на сервер

Очистка обновленных элементов управления HTML

Создание «теневых посылок» (cookies)

Запись данных на Web-страницу перед вызовом сценария

Имеются одно или два соглашения, которые я использую в этой книге, и о которых вам надо знать. Когда мне надо указать на конкретную строчку кода, я выделяю ее следующим образом:

$text = "Hello\n";

print $text;

А чтобы отделить результат работы сценария от собственно сценария, я выделяю его так:

$text = "Hello\n";

print $text;

Hello

Что еще вам потребуется

В этой книге я использую интерпретатор языка Perl версии 5.005. Perl является свободно распространяемым программным продуктом. Все, что вам надо сделать — это загрузить его из Интернета и установить (см. раздел «Как скопировать и установить Perl» в гл. 1). Если вы работаете в многопользовательской системе, в ней уже может быть установлен Perl. Чтобы проверить это, попробуйте выполнить команду

perl -v

которая выведет версию вашего Perl-интерпретатора.

Подсказка: Еще пара замечаний перед тем, как вы начнете самостоятельно работать с Perl. Я советую, чтобы вы использовали ключ -w в командной строке при запуске интерпретатора. В этом случае в процессе обработки сценария Perl при необходимости будет выводить предупреждающие сообщения (когда-нибудь это станет поведением интерпретатора по умолчанию). Второй совет: задавайте в сценарии прагму use strict — в этом случае Perl требует, чтобы все переменные были описаны в явном виде. Выполнение этих двух простых советов сэкономит вам удивительно много времени для отладки.

Вам также потребуется инструмент для создания Perl-сценариев. Сценарии — это просто текстовые файлы, содержащие команды и описания языка Perl. Чтобы создать сценарий для Perl, вам нужен текстовый редактор, который сохраняет редактируемые файлы в формате простого текста. (Относительно подробностей — см. раздел «Как написать сценарий для Perl» в главе 1.)

Отмечу, что вам не потребуется глубокое знание операционной системы Unix, для которой исходно создавался язык Perl. Хотя многие книги по Perl считают как данное, что вы являетесь программистом в среде Unix, для данной книги это не так. Perl далеко вышел за рамки Unix, и настало время, чтобы руководства по Perl признали этот факт<$FВ этом отношении книга устарела — практически все современные руководства по Perl не подвергают сомнению существование платформ, отличных от Unix (прежде всего, MS Windows). Несомненно, однако, что правильно Perl работает только для Unix-подобных операционных систем. — Примеч. ред.>.

Другие ресурсы

Существуют и другие ресурсы, которые могут помочь при работе с Perl. К интерпретатору прилагается обширная и полезная документация. В системах типа Windows эта документация предоставляется в виде связанных HTML-страниц. Для многопользовательских систем вы, как правило, получаете доступ к этой документации с помощью системных команд (подобно команде man операционной системы Unix).

Для программистов на Perl имеется также ряд телеконференций (групп новостей USENET):

comp.lang.perl.announce — группа с низким потоком сообщений.

comp.lang.perl.misc — группа с интенсивным потоком сообщений (сюда, в частности, рассылается FAQ-файл по Perl).

comp.lang.perl.modules — все, имеющее отношение к созданию модулей и многократно используемого кода.

comp.lang.perl.tk — о связях Perl и оконно-графической библиотеки Tk. Они поддерживают большое количество визуальных интерфейсных элементов (кнопки, меню и так далее). Вы можете использовать их в Perl, что становится довольно популярным.

Если вы интересуетесь CGI-программированием, взгляните на группу новостей

comp.infosystems.www.authoring.cgi — эта группа не содержит шаблона perl в своем имени, однако это хорошее место для обсуждения с другими разработчиками особенностей CGI-программирования на Perl.

В Сети имеется также многочисленные Web-страницы, посвященные Perl (случайный поиск по «Всемирной Паутине» возвращает более 1.527.903 страниц, на которых упоминается Perl):

Домашняя страница Perl — www.perl.com, здесь вы сможете найти исходный код интерпретатора Perl и готовые программы под различные операционные системы, документацию, модули, сообщения об ошибках, а также FAQ — список ответов на часто задаваемые вопросы (он находится на www.perl.com/perl/faq).

Чтобы загрузить сам Perl, его модули, расширения, и тонны других имеющих отношение к нему вещей, загляните в архив CPAN (Comprehensive Perl Archive Network) на www.cpan.org или www.perl.com/CPAN-local/CPEN.html. Это — огромный, объединяющий несколько узлов источник почти что всего, что имеет отношение к Perl. Если вы прогуляетесь по архиву CPAN то гарантированно найдете там нужный вам код. Все — от расширений языка Perl до обработки изображений, от модулей для работы в Интернет до интерфейсов к базам данных.

Институт Perl на www.perl.com — это некоммерческая организация, чьей целью является, по ее собственным словам, «поддержка Perl доступным, работоспособным и бесплатным для всех». Институт, собрав под своим крылом цвет содружества любителей Perl, обеспечивает серьезную поддержку обмена информацией между программистами на Perl.

Страница, посвященная самому языку Perl, находится на www.perl.com/perl/ (сюда же вас приведет ссылка language.perl.com). Здесь находятся обзоры, новости, списки ресурсов, программное обеспечение. Здесь же расположен каталог списков рассылки (mailing lists), посвященных Perl.

Многие узлы, специализирующиеся на таких вопросах, как обеспечение секретности, CGI-программирование, и т. д., содержат разделы, относящиеся к Perl — если не боитесь утонуть в потоке подобной информации просто проведите Web-поиск.

Также четыре раза в год издается печатный (бумажный) вариант журнала по языку Perl. Больше узнать о нем можно, заглянув на страничку orwant.www.media.mit.edu/the_perl_journal/<$FИли же на www.tpj.com. — Примеч. ред.>.

Итак, вся необходимая вводная информация дана — время приступить к изучению Perl. Начнем, как водится, с главы 1.

Часть I. Синтаксис Perl 

Глава 1. Основы Perl

Коротко 

В этой главе рассказывается об основе основ Perl — создании и запуске сценариев Perl. Навыки, полученные в этой главе, обеспечат читателю, не знакомому с Perl, фундамент, который потребуется для восприятия остальных частей книги. Кроме создания и запуска сценариев мы рассмотрим вывод текста на экран и ввод текста с клавиатуры. Возможно, читатель уже знаком с большей частью материала этой главы, в этом случае она может служить обзорной. Однако часть материала, по-видимому, будет новой. Например, немногие люди навскидку способны перечислить, что именно делает каждый из ключей, задаваемых в командной строке при запуске интерпретатора.

Язык Perl был создан в 1986 году как инструмент для администрирования и конфигурирования системных ресурсов в сети, состоящей из Unix-компьютеров. Постепенно Perl (чья аббревиатура расшифровывается как Практический Язык для Извлечения текстов и Генерации отчетов (Practical Extraction and Reporting Language) или же — нежно и ласково — как Патологически Эклектичный Язык для Распечаток Чепухи (Pathologically Eclectic Rubbish Listing)) эволюциониро вал в межплатформенный язык и оказался в центре внимания процветающего кибернетического сообщества.

(Вы можете спросить: почему «Perl», а не «Pearl», то есть «жемчужина»? Дело в том, что графический язык с именем Pearl к моменту создания Perl уже существовал. Кроме того, обратите внимание, что точная аббревиатура слов Practical Extraction and Reporting Language, при включении первых букв всех слов, будет представлять собой именно Pearl.)

Perl является интерпретируемым языком, предназначенным для сканирования текстовых файлов, извлечения из них информации и вывода, на основе полученных таким образом данных, текстовых отчетов. То есть программа perl (обратите внимание на отсутствие заглавной буквы) используется для выполнения сценариев Perl. (Хотя компиляторы Perl тоже существуют.) В этой главе мы начнем изучение сценариев Perl.

Некоторые люди удивляются популярности Perl (языка, ориентированного на текстовый ввод и вывод и запускаемого из командной строки) в мире графических интерфейсов типа Windows. Популярность Perl продолжает расти по ряду причин.

Глава 1. Основы Perl

Многие операционные системы остаются текстово-ориентированными.

· Perl является межплатформенным языком, максимально идентично поддерживаемым в разных операционных системах, и отличается только в нескольких неизбежных деталях (таких, как число байтов, используемых для представления длинного целого).

· На самом деле Perl обладает определенными графическими возможностями за счет взаимодействия с популярным модулем Tk.pm (мы рассмотрим его в главе 13). Данный модуль позволяет использовать стандартные графические интерфейсные элементы (widgets) с помощью вспомогательного средства — библиотеки Tk языка Tcl. Это позволяет создавать из Perl окна с кнопками, меню и другими объектами.

· Однако с точки зрения явного большинства программистов текущая популярность Perl подпитывается программированием Common Gateway Interface (CGI-программированием), применяемого для операций по взаимодействию клиент/сервер в среде Web. Когда речь идет о создании Web-страниц, текстовая ориентированность языка перестает быть недостатком, так как они также являются чисто текстовыми объектами. CGI-программирование на Perl представляет собой очень мощный инструмент, и, соответственно, это одна из основных тем, которые мы будем рассматривать.

А теперь перейдем к запуску нескольких сценариев.

Непосредственные решения 

Как скопировать и установить Perl

Perl является свободным программным продуктом. Достаточно скопировать и установить его. Если вы работаете на компьютере с несколькими пользователя ми или в многопользовательской системе, то Perl, возможно, уже установлен. Попробуйте выдать команду

%perl -v

в командной строке.

Подсказка. На протяжении этой книги знак процента (%) в начале строки означает приглашение командной строки для операционной системы Unix и вводить его не надо. 

Если Perl установлен и находится в одном из путей поиска, эта команда выведет номер версии и номер патча вашего интерпретатора (патчи Perl выпускаются регулярно для исправления отдельных ошибок).

Обратите внимание, что для некоторых систем интерпретатор Perl, используемый по умолчанию, относится к более ранней версии — например, к версии 4. Чтобы использовать Perl версии 5, вам потребуется команда типа perl5 (попробуйте ее, если perl -v выдает версию, отличную от 5):

%perl5 -v

Если Perl не установлен, вы можете найти его на www.perl.com или www.cpan.org. (CPAN — Comprehensive Perl Archive Network — самый полный электронный архив материалов, имеющих отношение к языку Perl. По мере чтения этой книги вы узнаете об этом ресурсе больше.) На этих узлах вы можете найти и загрузить все, что вам нужно.

Я намеренно не собираюсь описывать процессы инсталляции, которые нужно выполнить для разных операционных систем, чтобы установить Perl. Во-первых, эти процедуры тщательно детализированы и описаны на указанных узлах (например, руководство по инсталляции Perl для Unix находится на www.perl.com/CPAN-local/doc/relinfo/INSTALL.html). Во-вторых, они подвержены спонтанным изменениям, которые не могут быть отражены в книге. (Многие книги устарели за счет описания детальных инструкций по инсталляции — например, книги по языку Java, — поскольку эти инструкции изменялись чуть ли не моментально с появлением новой версии.)

Самая последняя версия Perl может быть получена, если выбрать ссылку «Get the latest version of Perl» на узле www.perl.com. Эта ссылка приведет вас к страничке, на которой перечислены версии Perl для наиболее популярных операционных систем (например, ActiveState Perl для Win32). Убедитесь, что вы получили версию 5.005 или более позднюю, поскольку предыдущие версии Perl для Win32 не вполне совместимы с Perl для Unix и его модулями.

Глава 1. Основы Perl

Как написать сценарий для Perl

Сценарии Perl представляют собой простые текстовые файлы, в которых находятся описания и команды. (Как вы увидите позднее, все, что вам надо сделать, — это описать форматы и подпрограммы.) Чтобы создать сценарий Perl, надо иметь текстовый редактор или текстовый процессор, который может сохранять редактируемые файлы в формате простого текста.

Сохранение файла в формате простого текста — элементарное действие, которое может оказаться за пределами возможностей хитроумного текстового процессора. Например, легко столкнуться с трудностями при работе с программой Microsoft Word, хотя и там можно сохранить результат редактирования как простой текст, если вы используете окно диалога File|Save As. Общее правило гласит: если при выводе файла на экран из командной строки (например, с помощью команды type) не появляется странных небуквенных символов, — это формат простого текста. Истинная проверка, естественно, будет в том, сможет ли Perl прочитать и скомпилировать ваш сценарий.

В этой книге для стандартных сценариев Perl используется расширение .pl. Например, сценарий, который будет приведен чуть позже, получит имя hello.pl. Сценарии Perl не требуют такого расширения (например, другое типичное расширение — это .p) и, более того, вообще могут без них обходиться. Тем не менее для Perl-сценариев традиционно используют расширение .pl. В частности, популярный интерпретатор ActiveState Perl для Win32 ассоциирует расширение .pl с интерпретато ром Perl, так что вы можете запускать сценарии автоматически, дважды щелкнув по имени файла. Естественно, никто не заставляет для сценариев использовать расширение .pl, равно как и вообще присоединять расширение к имени файла.

Убедитесь, что сценарий сможет найти Perl

Как вы узнаете из раздела «Выполнение сценариев Perl», существуют два основных способа запуска Perl-сценариев. Во-первых, можно запустить интерпретатор Perl в явном виде из командной строки

%perl hello.pl

Можно также настроить систему так, чтобы сценарий сам запустил интерпрета тор Perl. В этом случае сценарий выполняется командой типа:

%hello.pl

или в случае, когда текущий каталог не включается в путь поиска по соображениям безопасности, командой типа1:

%./hello.pl

Система настроек в различных операционных системах устроена по-разному.

1 Например, в Unix по указанным соображениям текущий каталог, как правило, не включается в список путей поиска. Поэтому если вы просто введете имя сценария в командной строке в виде «hello.pl», то ваш сценарий может быть выполнен лишь в том случае, если он находится в каталогах, куда обычному пользователю что-либо записывать запрещено (Unix попросту не сможет найти команду«hello.pl», если она не находится в системном каталоге). В силу этого данный авторский пример был нами слегка подкорректирован. — Примеч. ред. 

Убедитесь, что сценарий сможет найти Perl

Unix

Операционной системе Unix можно объяснить, что для запуска сценария надо вызвать интерпретатор Perl, если в первой строке файла находится следующий текст (учтите, что такой строки не требуется, если вы запускаете сценарий на выполнение обычным способом):

#!/usr/local/bin/perl # Use Perl

Строчка с такой специфической синтаксической конструкцией, как #!, обязатель но должна стоять в файле первой. Эта строка содержит ссылку на каталог, в котором на большинстве компьютеров с операционной системой Unix располагается интерпретатор Perl. Perl также может располагаться в другом месте — например, /usr /bin/perl (обратите внимание, что на многих машинах оба пути ссылаются на один и тот же файл). Чтобы выяснить, где находится Perl, используйте команду which perl.

Чтобы указать, что вам требуется Perl 5, для большинства систем можно использовать строку

#!/usr/local/bin/perl5 # Use Perl 5

При запуске интерпретатора, чтобы гарантировать вывод предупреждающих сообщений по мере обработки интерпретатором вашего кода, рекомендуется использовать ключ -w. (На самом деле интерпретатор Perl компилирует код целиком при его загрузке. Поэтому предупреждающие сообщения появятся сразу же, если только вы не загружаете откомпилированный код позже. Это можно сделать с помощью команды Perl require, которая загружает код во время выполнения сценария. Мы увидим, как это делается, в главе 15.)

#!/usr/local/bin/perl5 -w # Use Perl 5 with warnings

Поскольку во многих системах Unix строка с шаблоном #! обрезается после 32 знаков, при попытке ввести для Perl длинный путь вы встретитесь с проблемой:

#!/usr/local/bin/users/standard/build36/perl5

В подобных случаях, а также если ваша система не поддерживает синтаксичес кие конструкции вида !#, можно использовать вызов командного интерпретато ра sh с тем, чтобы запустить Perl «обычным путем»:

#!/usr/sh

eval `/usr/local/bin/perl5 -wS $0 ${1+"$@"}' if 0;

Здесь используется команда eval командного интерпретатора с тем, чтобы выполнить запуск Perl. Ключ -w обеспечивает вывод предупреждающих сообщений. Параметр $0 должен включать в себя полный путь, однако иногда этот механизм не работает. Поэтому ключ -S заставляет Perl при необходимости самостоятель но искать сценарий. Странная конструкция ${1+"$@"} обрабатывает имена с внутренними пробелами. Обратите также внимание, что наш пример запускает интерпретатор Perl, но не возвращает значения (кода завершения), так как оператор if 0 никогда не является истинным.

Глава 1. Основы Perl

Вы можете использовать строку типа

#!/usr/local/bin/perl5 -w

в начале всех сценариев. Однако заметьте, что для краткости (и потому, что большинство операционных систем такую конструкцию не поддерживают) в сценариях, которые мы будем разрабатывать в этой книге, такая строка будет опускаться. Если для ваших сценариев она требуется, добавьте ее сами.

Подсказка. Перед тем как запускать сценарий Perl под Unix в качестве команды (то есть просто указывая его имя в командной строке, например «./script.pl», а не «perl script.pl»), вам нужно присвоить ему статус исполняемого файла. Для этого просмотрите раздел «Выполнение сценариев Perl», который можно найти в этой же главе немного далее. 

MS-DOS

В операционной системе MS-DOS вы можете гарантировать, что сценарий найдет интерпретатор Perl, если преобразуете его в .bat-файл с помощью утилиты pl2bat.bat. (Она входит в комплект пакета ActiveState Perl.)

Например, если у вас есть сценарий hello.pl:

print "Hello!\n";

print "Press <Enter> to continue...";

<STDIN>;

то с помощью команды

C:\>pl2bat hello.pl

вы преобразуете его в файл hello.bat. Результирующий файл будет выглядеть следующим образом:

@rem = `--*-Perl-*--

@echo off

if "%OS%" == "Windows_NT" goto WinNT

perl -x -S "%0" %1 %2 %3 %4 %5 %6 %7 %8 %9

goto endofperl

:WinNT

perl -x -S "%0" %*

if NOT "%COMSPEC%"=="%SystemRoot%\system32\cmd.exe" goto endofperl

@rem `;

#!perl

#line 14

print "Hello!\n";

print "Press <Enter> to continue...";

<STDIN>;

__END__

:endofperl

Как написать программу Perl: команды и описания

Windows 95/98 и Windows NT

Пакет ActiveState Perl для Windows 95/98 и Windows NT модифицирует реестр Windows так, что файлы с расширением .pl ассоциируются с интерпретатором Perl. Двойной щелчок мышью на файле сценария запускает его на выполнение. Однако когда это происходит, открывается окошко MS-DOS, в нем запускает ся интерпретатор Perl, а затем окно MS-DOS немедленно закрывается (до того, как вам удается прочитать выведенный текст). Для того чтобы избежать этого эффекта, посмотрите раздел «Как избежать немедленного закрытия окна MSDOS в Windows 95/98/NT», который можно найти в этой же главе немного ниже.

Macintosh

На компьютерах Macintosh сценарии Perl выполняются автоматически — для вызова Perl дважды щелкните на сценарии мышью.

Как написать программу Perl: команды и описания

Программа на Perl состоит из команд и описаний. Описания указывают Perl, как вы собираетесь использовать определенные программные конструкции до того, как это произойдет на самом деле. Они нужны только для форматов и подпрограмм, хотя можно также описывать и другие элементы типа переменных. Более подробно об описаниях речь пойдет в этой книге дальше.

Команды встречаются в двух формах: простой и составной. Простая команда — это выражение, выполняющее некоторое конкретное действие. В программе простые команды заканчиваются точкой с запятой (;), как происходит в следующем примере, где функция print используется для вывода на экран строки Hello!, завершаемой символом перевода строки \n (см. раздел «Основные команды форматирования» далее в этой главе):

print "Hello!\n";

Составные команды состоят из выражений и блоков. Блоки в языке Perl ограничиваются фигурными скобками ({) и (}) и могут содержать несколько простых команд. Они также имеют свои области видимости (область видимости элементов типа переменных — это сегмент программы, в котором можно использовать переменную; более подробно этот вопрос рассматривается далее). После закрывающей фигурной скобки не надо ставить точку с запятой.

Далее следует пример блока, с помощью которого создается составной оператор цикла for (это фундаментальный оператор цикла в Perl, мы будем подробно его исследовать в главе 5):

for ($loop_index = 1; $loop_index <= 5; $loop_index++) {

print "Hello!";

print "there!\n";

}

Выполнение сценариев Perl

Предположим, что имеется файл hello.pl со следующим сценарием на Perl:

#!/usr/local/bin/perl5 -w # Use Perl5 with warnings

print "hello\n";

как его выполнить? Это основная операция при работе с Perl. Однако, поскольку имеется несколько вариаций на основную тему, мы сделаем краткий обзор имеющихся возможностей.

Как ваш сценарий может найти Perl сам

Если ваш сценарий может запустить интерпретатор Perl самостоятельно (см. раздел «Убедитесь, что сценарий сможет найти Perl» ранее в этой главе), вам легко его выполнить. Для Unix это значит, что первая строка файла содержит текст типа #!/usr/local/perl5 -w. Кроме того, сценарий надо сделать исполняемым файлом. Это осуществляется с помощью команды

chmod +x hello.pl

Также следует убедиться, что сценарий расположен в одном из путей поиска.

Например, для этого следует проверить ваш файл .login и провести поиск команд set path, если в качестве командной оболочки используется csh или одна из его производных. Если же для этой цели используется sh или аналогичный интерпретатор, проверьте команду PATH. В случае применения другого типа оболочки используйте ее специальные команды (в крайнем случае, сверьтесь со справочником). После этого запустите сценарий на выполнение, введя в командной строке команду типа:

%hello.pl

В операционных системах Windows или Macintosh, чтобы запустить сценарий, нужно дважды щелкнуть на его имени. Убедитесь, что в случае Windows файл имеет расширение .pl, поскольку пакет ActiveState Perl использует именно это расширение для ассоциирования файлов скриптов с интерпретатором Perl.

Если вы работаете в операционной системе MS-DOS, то, преобразовав с помощью утилиты pl2bat.bat Perl-сценарий к форме командного файла (см. раздел «Убедитесь, что сценарий сможет найти Perl» ранее в этой главе), просто запустите этот файл из командной строки

C:\>hello.bat

Как использовать командную строку

Чтобы запустить сценарий на выполнение с помощью вызванного в явном виде интерпретатора, убедитесь, что программа с именем perl находится в одном из путей поиска. Затем введите в командной строке команду perl. Она может иметь следующий синтаксис:

perl [-sTuU] [-hv] [-V[:configvar]] [-cw]

[-d[:debugger]] [-D[number/list]] [-pna]

Выполнение сценариев Perl

[-Fpattern] [-l[octal]] [-O[octal]] [-Idir]

[-m[-]module] [-M[-]'module...'] [-P] [-S]

[-x[dir]] [-i[extension]] [-e `command']

[--] [programfile] [arguments]

(Ключи в квадратных скобках являются необязательными. Об их назначении речь пойдет далее в разделе «Ключи командной строки» этой главы.)

При запуске интерпретатора Perl сценарий ищется следующим образом:

· Если задан ключ -e, то команды для Perl указываются в командной строке следом за этим ключом.

· Сценарий берется из файла, который стоит первым в списке параметров командной строки (в нашем примере это [programfile]).

· Если в качестве имени файла задан дефис (_), то сценарий считывается построчно из стандартного потока ввода.

Рассмотрим каждый из этих способов.

Использование ключа -e позволяет задавать команды Perl и запускать интерпретатор из одной и той же командной строки. (В некоторых системах вы можете использовать несколько ключей -e, чтобы передать несколько блоков команд.) Например:

%perl -e `print "Hello!\n";'

Hello! 

Однако с кавычками надо быть осторожнее, так как в разных операционных системах они работают по-разному. Вот, например, как выглядит та же самая команда в MS-DOS:

c:\>perl -e "print \"Hello!\n\";"

(Обратите внимание на escape-последовательности \", заменяющие двойные кавычки в теле команды Perl. Более подробно эти конструкции рассматриваются в разделе «Основы форматирования текста» далее в этой главе.)

Конечно, можно поместить сценарий в текстовый файл и передать интерпрета тору имя файла. Например, если содержимым файла hello.pl является

print "Hello!\n";

(магическая строчка с шаблоном !# опущена, так как интерпретатор запускается в явном виде), то этот сценарий запускается как

%perl hello.pl

Hello! 

Можно также вводить команды Perl построчно с клавиатуры, если вместо имени файла указан дефис:

%perl -

(Здесь даже дефис можно опустить, поскольку такой метод предполагается по умолчанию.) Тогда интерпретатор будет ждать, пока вы не введете сценарий:

%perl -

print "Hello!\n";

Интересный вопрос: а как интерпретатор определит, что сценарий введен и пора приступить к его выполнению? Это произойдет, когда на экране появится инструкция __END__, означающая конец текста:

%perl -

print "Hello!\n";

__END__

Hello! 

Такой метод означает выполнение всего сценария целиком. При тестировании эффективнее вводить команды по очереди и выполнять их в интерактивном режиме. Для этого необходимо создать мини-интерпретатор Perl (на Perl J), чем мы и займемся в следующем разделе.

Интерактивное выполнение сценариев Perl

Во время тестирования может потребоваться последовательно выполнять команды Perl по мере их ввода и наблюдать на экране результат их действия. Для этого необходимо написать Perl-оболочку. Вот небольшой работающий пример:

#!/usr/local/bin/perl5 -w # Use Perl 5 with warnings

use strict; # Require variable declarations, etc.

my $count=0; # $count used to match {}, (), etc.

my $statement=""; # statement holds multi-line commands

local $SIG{__WARN__} = sub{}; # supress error reporting

while (<>) { # Accept input from the keyboard

chomp; # Clean-up input

while (/{|\(|\[/g) {$count++}; # Watch for {, (, etc.

while (/}|\)|\]/g) {$count--}; # Watch for }, ), etc.

$statement .= $_; # Append input to current statement

if (!$count) { # Evaluate only if {, ( matches }, )

eval $statement; # Evaluate Perl statement

if ($@) {print "Syntax error.\n"}; # If error ...

$statement = ""; # Clear the current statement

$count = 0; # Clear the counter

}

}

Этот сценарий представляет собой простейшую Perl-оболочку, которая может обрабатывать составные команды, в том числе и те, что охватывают несколько строк ввода. Он работает за счет вызова функции Perl eval, которая вычисляет выражение, переданное ей в качестве аргумента. Эта программа также запоминает вводимый текст (разбитый на несколько строк ввода) до тех пор, пока число открывающих скобок не будет соответствовать числу закрывающих скобок, и лишь затем обращается к функции eval. (До некоторой степени это гарантирует, что разбитое на несколько строк выражение действительно введено полностью. Однако внимательный читатель, безусловно, заметит, что такая оболочка не делает различия между различными типами скобок и не проверяет правильную их вложенность.) Например, можно запустить этот сценарий и ввести команду, ко

Ключи командной строки

торая заносит текст в переменную Perl (относительно переменных — см. следующую главу):

$text = "Hello!\n";

а затем распечатать ее:

$text = "Hello!\n";

print $text;

Результат появится на экране немедленно:

$text = "Hello!\n";

print $text;

Hello! 

Точно так же можно проверить, как выполняются многострочные команды (так как каждая команда выполняется, когда она полностью введена, результат работы команды print появляется на экране мгновенно):

$variable1 = 1;

$variable2 = 2;

print $variable1 + $variable2;

4 

Аналогичным образом обрабатываются составные команды, занимающие более одной строчки:

for ($loop_index = 1; $loop_index <= 5; $loop_index++) {

print "Hello!\n";

}

Hello!

Hello!

Hello!

Hello!

Hello! 

Чтобы выйти из оболочки, введите команду exit.

Стандартные оболочки Perl (подобные данной) бывают полезны при тестирова нии коротких сценариев, ибо не требуют создания отдельных файлов и их загрузки в интерпретатор. (Обратите внимание, что данная оболочка — лишь пример, а никоим образом не законченная оболочка Perl. В частности, если тестовый сценарий содержит команду eval, вы встретитесь с серьезными проблемами, так как оболочка сама использует команду eval для пошагового выполнения команд Perl.)

Ключи командной строки

Интерпретатор Perl может использовать впечатляющее количество дополнитель ных ключей, управляющих его работой:

perl [-sTuU] [-hv] [-V[:configvar]] [-cw]

[-d[:debugger]] [-D[number/list]] [-pna]

[-Fpattern] [-l[octal]] [-0[octal]] [-Idir]

[-m[-]module] [-M[-]'module...'] [-P] [-S]

[-x[dir]] [-i[extension]] [-e `command']

[--] [programfile] [arguments] ...

(как обычно, квадратные скобки указывают на необязательность соответствую щей конструкции).

Итак, что же делают все эти ключи? Они перечислены здесь в алфавитном порядке (многие, однако, предоставляют возможности, которые мы обсудим позднее):

· -0[цифры] — задает разделитель входных записей как символ, заданный восьмеричным представлением (этот разделитель также содержится в специальной переменной Perl $/). По умолчанию в качестве разделителя выступает символ \n. Если ключ -0 задан без последующих цифр, то используется символ \0.

· -a — включает режим авторазбивки (действует только при наличии ключей p и -n). При этом режиме входные строки разбиваются на фрагменты по границе слова и помещаются в специальный массив @F (то есть вместо скалярной переменной $_ надо будет использовать его).

· -c — заставляет Perl проверять синтаксис входного сценария и завершать работу без его выполнения. Впрочем, блоки BEGIN и END (конструкторы и деструкторы модулей) будут выполнены и в этом случае.

· -d — запускает сценарий под управлением встроенного отладчика Perl.

· -d:имя — запускает сценарий под управлением отладчика или трассировщи ка, инсталлированного как Devel::имя (то есть отладчик существует как подмодуль модуля Devel, имеющий указанное имя — см. главу 15).

· -D[число/список] — устанавливает флаги режима отладки (аргумент — двоичная маска или список флагов). Более подробно флаги отладки рассматрива ются в главе 17.

· -e команда или -e 'команды... ' — используется для непосредственного выполнения команд Perl, задаваемых прямо в командной строке. В некоторых системах можно использовать одновременно несколько ключей -e, разбивая список команд Perl на блоки.

· -Fшаблон или -F/шаблон/ — задает шаблон для разбивки входных строк на блоки, когда заданы ключи -n или -p, а также ключ -a. Символы наклонной черты, окружающие шаблон разбивки, необязательны.

· -h — выводит краткий список ключей с пояснениями.

· -i или -iрасширение — разрешает редактировать «на месте» файлы, открытые из стандартного потока ввода (конструкция <> — см. далее раздел «Чтение потока ввода»). Режим редактирования «на месте» означает, что изменения вносятся непосредственно в открытый файл, когда вы выводите данные в поток ввода STDIN. Если после ключа указано расширение, то Perl обеспечивает резервную копию редактируемого файла, создавая файл c заданным расширением и тем же самым именем. Если же расширение не указано, то временная копия файла, созданная интерпретатором, удаляется после завершения работы.

Ключи командной строки

· -Iкаталог — задает каталог, в котором Perl ищет модули. В командной строке может быть задано несколько параметров -I.

· -l — управляет обработкой символов начала новой строки. Если этот ключ задан вместе с -p или -n, Perl при вводе автоматически удаляет символ начала новой строки (заданный в специальной переменной $/). Одновременно при выводе оператором print тот же символ добавляется в конец строки.

· -l[цифры] — в дополнение к функциям предыдущего ключа позволяет задать в явном виде символ, выступающий в качестве начала новой строки (и занести его в переменную $/) — для этого достаточно задать восьмеричное число, определяющее код символа. По умолчанию в качестве разделителя строк используется символ \n.

· -m[-]модуль , -M[-]модуль или -M[-]'модули...' — без знака дефиса этот ключ подключает указанные модули. Если ключ используется со знаком дефиса, указанные модули исключаются из сценария. Действие ключа без дефиса аналогично прагме Perl «use модуль» в начале сценария. Соответственно, действие ключа с дефисом аналогично прагме «no модуль». При старте интерпретатора в командной строке можно указывать несколько параметров -m или -M.

· -n — Perl считает, что сценарий заключен в цикл while (<.>)(см. далее раздел «Чтение потока ввода» относительно конструкции <>). Например, следующая команда выводит на экран содержимое файла file.txt:

perl -ne "print;" file.txt

· -p — Perl считает, что сценарий заключен в следующий цикл (его работа имитирует редактор sed):

while (<>) {

...

[сценарий]

...

} continue {

print or die "-p destination: $!\n";

}

· -P — пропустить сценарий через препроцессор компилятора C. Это позволяет использовать директивы C вида #define и #include, а также операторы условной компиляции. После препроцессора результат поступает на ввод Perl.

· -s — разрешает разобрать ключи, переданные сценарию в качестве параметра (список [arguments], указываемый в командной строке после имени файла). Например, следующая команда печатает сообщение «Found the switch», если при старте сценарию был задан ключ -www:

if ($www) {print "Found the switch\n"};

· -S — заставляет Perl использовать переменную окружения PATH для поиска сценария.

· -T — активизирует проверку меченых данных (проверка секретности доступа). Часто требуется как составная часть при CGI-программировании.

· -u — заставляет Perl записать дамп памяти (core dump) после компиляции сценария.

· -U — разрешает Perl выполнять «небезопасные» операции (например, удаление каталогов).

· -v — выводит номер версии, подверсии и номер патча Perl, а также платформо-зависимую информацию об интерпретаторе (последняя может быть очень существенной).

· -V — подробная распечатка текущей конфигурации Perl (то есть всех конфигурационных переменных).

· -V:имя — распечатка конфигурационной переменной с указанным именем.

· -w — выводить предупреждающие сообщения (см. также следующий ключ). Рекомендуется всегда запускать Perl с этим ключом.

· -x или -xкаталог — указывает Perl, что сценарий содержится внутри сообщения. Ввод не будет обрабатываться, пока интерпретатор не встретит строчку с шаблоном !# и подстрокой perl. Конец обрабатываемого текста совпадает с концом файла или синтаксической конструкцией __END__. Если указан каталог, то Perl выполняет команду перехода к каталогу перед запуском сценария.

· -- — эти два дефиса являются необязательными. Они означают конец списка ключей, передаваемых интерпретатору.

Ключ -w и проверка синтаксиса

При работе с Perl стоит всегда задавать ключ -w. Большинство знатоков стиля языка Perl фанатично поклоняются ему. Я также рекомендую указывать этот ключ.

Ключ -w выводит предупреждающие сообщения по многим поводам. Среди них:

· имена переменных, упоминающихся только один раз,

· скалярные переменные (вроде простых переменных), которые используются до инициализации,

· переопределение подпрограмм,

· ссылки на неопределенные дескрипторы файлов,

· дескрипторы файлов, открытых только для чтения, но для которых производится попытка записи,

· значения, используемые как числа, но выглядящие иначе, чем числа,

· использование массива как скалярной переменной,

· подпрограммы с глубиной рекурсии больше 100.

Текстовый ввод и вывод с помощью стандартных дескрипторов файлов

Perl рассматривает ввод и вывод данных как потоки, а работа с этими потоками организуется через дескрипторы файлов. Дескриптор файла — это просто значение, которое в Perl соответствует файлу. Программа получает дескриптор, когда открывает файл.

Печать номера текущей строчки сценария и имени сценария

Для работы с текстом используются три предопределенных дескриптора:

· STDIN — стандартный поток ввода.

· STDOUT — стандартный поток вывода.

· STDERR — стандартный поток вывода для сообщений об ошибках.

По умолчанию все три файла соответствуют терминалу. В этой главе (например, в следующем разделе) мы ограничимся предопределенными дескрипторами файлов, используя для вывода текста STDOUT.

Вывод текста

Чтобы вывести текст в файл (включая стандартный поток вывода STDOUT), используется команда print. Она имеет три формы:

print дескриптор список 

print список 

print

Если дескриптор файла не задан, используется STDOUT. Если не задан список (который может состоять и из одного элемента), Perl выводит содержимое специальной переменной $_. (Она по умолчанию используется для сохранения результатов последней операции чтения из потока ввода — см. далее раздел «Чтение потока ввода».)

Простейший пример, в котором в поток вывода отправляется слово «Hello» и символ конца строки:

print "Hello!\n";

Hello! 

Функция print работает со списком аргументов (списки Perl будут рассмотрены в следующей главе). Это значит, что ей можно подать на вход список элементов, разделенных запятыми, и они будут напечатаны:

print "Hello", "there!\n";

Hello there! 

Отметим, что в Perl текстовый вывод может быть довольно изощренным, благодаря специальным форматам и команде printf (см. главу 11).

Печать номера текущей строчки сценария иименисценария

Чтобы использовать номер строчки сценария, которую в данный момент обрабатывает Perl, надо сослаться на него с помощью конструкции __LINE__. Точно так же можно вывести имя файла, в котором хранится сценарий, если использовать конструкцию __FILE__. Пример:

%perl -e "print __LINE__;"

1 

Повтор текста при печати

Можно повторить вывод строки несколько раз, не копируя ее в теле сценария и не используя оператор цикла. Для этого служит оператор повторения, который выглядит следующим образом:

print "Hello!\n" x 5;

Hello!

Hello!

Hello!

Hello!

Hello! 

Например, вот так выводится горизонтальная черта из дефисов:

print "-" x 30;

------------------------------ 

Основные команды форматирования

С помощью escape-последовательностей в Perl можно выполнять некоторые команды форматирования. Escape-последовательность — это набор символов, которым предшествует обратная косая черта (\). Она особым образом обрабатывает ся внутри строки, ограниченной двойными кавычками (разница между строками, ограниченными двойными кавычками, и строками, ограниченными апострофами, рассматривается в следующей главе). Некоторые escape-последовательности приведены в табл. 1.1.

Например, вот как напечатать двойные кавычки в текстовой строке:

print "\"Hello!\"\n";

"Hello!"

Подсказка. Символы \, $ и комбинации вида @идентификатор и @{ также надо набирать с помощью escape-последовательностей, так как внутри строки, ограниченной двойными кавычками, эти символы интерпретируются особым образом (в частности, символ \ — как начало escape-последовательности.) 

А вот так используется табуляция:

print "Hello\tfrom\tPerl.\n";

Hello from Perl. 

Здесь с помощью символов новой строки выводится сразу несколько строчек:

print "Hello\nfrom\nPerl.\n";

Hello

from

Perl. 

Это лишь базовые команды форматирования, и в Perl возможны гораздо более сложные конструкции. В этом языке всегда существует еще один способ выполнения той или иной операции с текстом, — в конце концов, он создавался именно как язык для обработки текстов.

Вывод неформатированного текста: встроенные документы

Таблица 1.1. Некоторые escape-последовательности

Символ Значение

\' Одиночная кавычка, или апостроф (')

\` Обратный апостроф (')

\" Двойная кавычка (")

\\ Обратная косая черта (\)

\$ Символ доллара ($)

\@ Символ at-коммерческое (@)

\e Символ escape (ESC)

\t Символ табуляции (TAB, HT)

\v Символ горизонтальной табуляции (VT)

\n Символ новой строки (LF)

\r Символ возврата каретки (CR)

\f Символ прогона страницы (FF)

\b Символ забоя (BS)

\a Символ звукового сигнала (BEL)

\033 Восьмеричный символ

\x1b Шестнадцатеричный символ

\c[ Управляющий символ (control character)

Вывод неформатированного текста: встроенные документы

Perl позволяет выводить текст в точности так, как он набран в теле сценария. Чтобы отметить начало подобного текстового фрагмента (в Perl они называются «here-documents»), используется команда <<, за которой следует некоторая метка (в нашем примере это EOD, что является сокращением от end-of-document):

print <<EOD;

This

is

a

"here"

document.

EOD 

Текст, отформатированный в теле сценария, заканчивается идентификатором, указанным в его начале. (Обратите внимание, что в первой строке после идентификатора, маркирующего начало «встроенного» текста, стоит точка с запятой, а после идентификатора, маркирующего конец текста, точки с запятой нет.) В результате на экран будет выведено:

This

is

a

«here»

document. 

Комментарии

При создании сложных сценариев нужно добавлять комментарии — своеобраз ные напоминания самому себе, что именно делает данный код. Это делает структуру сценария более прозрачной и облегчает работу с ним. При этом сам Perl комментарии игнорирует.

Комментарии Perl начинаются с символа #. Perl игнорирует весь текст, идущий от символа # до конца строки. Вот пример сценария Perl-оболочки с комментариями, разъясняющими назначение кода:

#!/usr/local/bin/perl5 -w # Use Perl 5 with warnings

use strict; # Require variable declarations, etc.

my $count=0; # $count used to match {}, (), etc.

my $statement=""; # statement holds multi-line commands

local $SIG{__WARN__} = sub{}; # supress error reporting

while (<>) { # Accept input from the keyboard

chomp; # Clean-up input

while (/{|\(|\[/g) {$count++}; # Watch for {, (, etc.

while (/}|\)|\]/g) {$count--}; # Watch for }, ), etc.

$statement .= $_; # Append input to current statement

if (!$count) { # Evaluateonly if {, ( matches }, )

eval $statement; # Evaluete Perl statement

if ($@) {print "Syntax error.\n"}; # If error ...

$statement = "; # Clear the current statement

$count = 0; # Clear the counter

}

}

Подсказка. Вы можете включить прямо в тело программы документацию (POD = Plain Old Document), описывающую, как надо работать с вашей программой или модулем. Она будет игнорироваться при загрузке, но вы всегда можете извлечь ее в виде ASCII-файла или даже в виде размеченного HTML-файла с помощью утилит, входящих в состав Perl. Как это сделать, рассказывается в главе 15 (раздел «Документирование модулей»). 

Чтение потока ввода

Ранее в этой главе мы видели, что для вывода текста используется функция print, — но как ввести текст? Его можно читать из стандартного потока ввода STDIN с помощью угловых скобок < и >. В следующем примере цикл while (рассматриваемый в главе 5) помогает построчно считывать вводимый пользовате лем текст, запоминать его в переменной $temp и затем выводить на экране:

while ($temp = <STDIN>) {

print $temp;

}

Если запустить этот сценарий и ввести с клавиатуры слово Hello, сценарий повторит его на экране:

Hello!

Hello! 

Очистка введенного текста

На самом деле (как это обычно и бывает в Perl) добиться подобного эффекта можно и более простым путем. (В Perl имеется специальное жаргонное выражение: «There is more than one way to do it» — «Сделать что-либо можно несколькими способами», сокращаемое до TMTOWTDI и произносимое примерно как «Tim Toady».) Для этого придется заглянуть в следующий раздел.

Специальная переменная $_ 

Когда конструкция <STDIN> используется без указания, куда поместить возвращаемое значение, Perl автоматически записывает его в специальную переменную $_. Многие функции Perl по умолчанию получают аргументы из нее (если пользователь не задал другого входного значения). Например, команда print без аргументов выведет содержимое именно $_. (Есть также масса других специальных переменных — например, $!, которая хранит информацию о последней ошибке, если та произошла. Более подробно о специальных переменных рассказыва ется в главе 9.)

На самом деле ключевое слово STDIN в угловых скобках можно опустить — для пустых угловых скобок по умолчанию предполагается дескриптор STDIN. (В Perl имеется масса правил по умолчанию, подобных этому. Это делает программы проще для экспертов, но запутаннее для новичков. Возможно, именно поэтому Perl так нравится экспертам.) Тем самым код из предыдущего раздела может быть записан как

while(<>){print;}

Это воистину краткая запись для аналогичных действий сценария

while ($_ = <STDIN>) {

print $_;

}

Очистка введенного текста

Через стандартный поток STDIN читается все, что набирается с клавиатуры или поступает из файла, включая символ новой строки в конце. Чтобы избавиться от него, можно использовать функцию chop или chomp. Вот как выглядит функция chop:

chop переменная 

chop список 

chop

Она отсекает последний символ в строке и возвращает его в качестве результата. Если опущено имя переменной, функция работает со специальной переменной $_. Например, сценарий

while (<>) {

print;

}

выводит на экране содержимое потока ввода, включая символы новой строки. Если же использовать такой вариант:

while (<>) {

chop; 

print;

}

то символов-паразитов новой строки не будет.

Вместо функции chop рекомендуется использовать chomp, которая вызывается аналогичным образом:

chomp переменная 

chomp список 

chomp

Эта функция более безопасна — она удаляет из конца строки символы, соответствующие текущему значению специальной переменной Perl $/, хранящей символ, выступающий в качестве разделителя входных записей. По умолчанию используется символ новой строки \n. Функция chomp возвращает число удаленных символов. Обычно она используется для удаления символа новой строки из записи, прочитанной через поток ввода. Если отсутствует имя переменной, обрабатывается $_.

Как избежать немедленного закрытия окна MS-DOS в Windows 95/98/NT

Если вы используете Perl для Windows 95/98 или Windows NT то, очевидно, отметили одну раздражающую деталь. После двойного щелчка по имени файла с расширением .pl появляется окно сеанса MS-DOS; в нем выполняется сценарий, но немедленно по его завершении окно закрывается, не оставляя никакого шанса просмотреть выведенную информацию.

Это можно исправить, если заставить сценарий ждать ввода с клавиатуры после завершения. Для этого достаточно добавить в конец программы две строчки:

print "Hello!\n";

print "Press <Enter> to continue...";

<STDIN> 

Естественно, <STDIN> можно заменить просто на <>:

print "Hello!\n";

print "Press <Enter> to continue...";

<>

Глава3. Массивы, хеши и записи таблицы символов

Коротко

В этой главе будет предпринята попытка выяснить, как организованы данные в таких крайне важных структурах, как массивы и хеши. Кроме того будет описана работа с другим важным типом данных — записями таблицы символов (typeglob).

Массивы

Массивы представляют собой состоящие из скаляров списки с целочисленным индексом. Индекс позволяет ссылаться на скаляры, занесенные в массив — это очень полезно для программирования, так как позволяет увеличивать или уменьшать индекс и получать доступ из программы к любому элементу, работая сразу со всем массивом. Для создания массива необходимо присвоить переменной-массиву в качестве значения список (в Perl такие переменные начинаются с префикса @):

@array = (1, 2, 3);

Чтобы ссылаться на отдельные элементы массива следует указать индекс элемента в квадратных скобках и заменить префикс @ на префикс $ — это показывает, что мы работаем со скаляром. Обратите также внимание, что индексы для массивов Perl отсчитываются от нуля:

print $array[0];

1

В этой главе основное внимание будет уделяться стандартным одномерным массивам. В главе 13, речь в которой идет о структурах данных, рассказывается также о двумерных массивах.

Хеши

Хеш-таблицы (хешированные таблицы, или просто хеши), называемые также ассоциативными массивами для доступа к отдельным элементам данных используют не индексы, а ключи. При использовании хешей значения ассоциируются с текстовыми ключами, например,

$hash{fruit} = apple;

$hash{sandwich} = hamburger;

$hash{drink} = bubbly;

Теперь можно использовать эти данные, применив для доступа к ним ключ:

print $hash{sandwich};

hamburger

Представление ваших данных в виде хешей, как правило более интуитивно (в отличие от массивов), поскольку для извлечения данных используются ключи. Хеши являются идеальным средством для создания записей данных.

Записи таблицы символов typeglob

Тип данных typeglob — еще один встроенный тип Perl. Разыменовывающий префикс для него — звездочка * . Тип данных typeglob используется для создания синонимов для типов данных, ассоциированных с указанным именем. (Соответственно, звездочка, которая является также и универсальным шаблоном при выборе файлов, отражает дух. нового типа данных.) Например, при наличии переменных $data и @data:

$data = "Here’s the data";

@data = (1, 2, 3);

с помощью конструкции typeglob можно ссылаться на эти переменные под другим именем:

*alsodata = *data;

Теперь $alsodata является синонимом для $data, а @alsodata — для @data:

print "$alsodata\n";

Here’s the data

Подсказка: Тип данных typeglob *имя, — это внутренняя структура (запись таблицы символов), хранящая информацию обо всех переменных с именем имя, (то есть, о скалярной переменной $имя, массиве @имя, хешее %имя, подпрограмме &имя, дескрипторе файла >имя). В частности, для каждого типа данных в таблицу символов записывается адрес области памяти (ссылка), где оно хранится. Присвоив одной записи содержимое другой записи, мы просто заставили ссылки двух таблиц ссылаться на одну и ту же область памяти.

На этом с введением покончено. В следующем разделе эти типы данных рассматриваются более подробно.

Непосредственные решения

Создание массивов

Имена массивов начинаются с символа @. Чтобы создать массив, следует присвоить переменной массива в качестве аргумента список:

@array = (1, 2, 3);

Чтобы увидеть результат, выведем новый массив:

@array = (1, 2, 3);

print @array;

123

Подсказка: Команда print интерпретирует массив как список и объединяет его элементы в единое целое. Более красивые способы вывода содержимого массива вы найдете в разделе «Вывод массивов».

На отдельные элементы массива можно ссылаться, указывая индекс в квадратных скобках и заменяя префикс имени символом $ (потому что элемент массива — скаляр):

@array = (1, 2, 3);

print $array[0];

1

Кроме чисел, в массиве можно хранить и другие скаляры, например, строки:

@array = ("one", "two", "three");

print @array;

onetwothree

Поскольку Perl при обработке списков пропускает все пробельные символы (включая символы новой строки), аргумент в виде списка значений можно легко разбить на несколько строчек:

@array = (

"one", "two", "three",

"four", "five", "six",);

print @array;

onetwothreefourfivesix

Можно также использовать оператор повторения, что в данном случае создаст массив из ста нулей:

@array = (0) x 100;

Наконец, можно использовать операторы-кавычки (см. раздел «Обработка кавычек и слов без кавычек» и таблицу 2.3 в предыдущей главе):

@array = qw(one two three);

print @array;

onetwothree

Кроме описанных способов, для создания массивов и добавления в них новых элементов, используются также функции push и unshift (они рассматриваются ниже в этой главе).

Хотя элементы в массивах Perl по умолчанию отсчитываются с нуля, можно изменить это правило, занеся новое базовое значение в специальную переменную $[. Ее использование, однако, считается дурным тоном. Хоть этот способ и не запрещен, вряд ли его кто-то одобрит. (В этом отличие Perl от языков типа Java, в которых методы, вызывающие неодобрение, просто запрещаются.)

Использование массивов

После создания массива можно ссылаться на его отдельные элементы, как на скаляры, указывая индекс в квадратных скобках и используя префикс $ перед именем массива:

@array = ("one", "two", "three");

print $array[1];

two

Поскольку для доступа к элементам массива можно использовать индекс, массивы могут работать как таблицы подстановок. Так в следующем примере, десятичное число из диапазона от 0 до 15, вводимое пользователем, заменяется на его шестнадцатеричный эквивалент:

@array = (’0’, ’1’, ’2’, ’3’, ’4’, ’5’, ’6’, ’7’,

’8’, ’9’, ’A’, ’B’, ’C’, ’D’, ’E’, ’F’);

while (<>) {

$hex = $array[$_];

print "$hex\n";

}

С точки зрения программиста тот факт, что массивы обеспечивают доступ к элементам данных по номеру, является мощным средством, позволяющим последовательно обрабатывать массив, изменяя в цикле индекс. Например, так выводятся все элементы массива по очереди:

@array = ("one", "two", "three");

for ($index = 0; $index <= $#array; $index++) {

print $array[$index], " ";

one two three

Обратите внимание на использование конструкции $#array. В Perl она означает последний индекс массива @array (см. также далее раздел «Определение длины массива»).

Операции push и pop

Кроме присвоения списком, для изменения массивов можно использовать функции push и pop. Функция push добавляет один или несколько элементов в конец массива:

push массив, список

Значения списка добавляются в конец массива, а длина массива увеличивается на длину списка.

Функция pop удаляет данные из массива:

pop массив

pop

Она удаляет и возвращает в качестве своего результата последнее значение в массиве, сокращая длину массива на один элемент. Следующий пример показывает, как последовательно добавляются элементы в (изначально пустой) массив:

push (@array, "one");

push (@array, "two");

push (@array, "three");

print $#array, ’:’, @array;

2:onetwothree

(обратите внимание, что $#array на единицу меньше числа элементов массива). Аналогичным образом удаляются данные из конца массива:

@array = ("one", "two", "three");

$variable1 = pop (@array);

print "$variable1/$#array";

three/1

Операции shift и unshift

Функции shift и unshift работают с началом массива так же, как push и pop с его концом. Вот как используется shift:

shift массив

shift

Эта функция удаляет первый элемент массива, возвращая его как результат. Массив сокращается на один элемент, а остальные элементы сдвигаются на одну позицию вправо.

А вот как выглядит функция unshift:

unshift массив, список

Эта функция выполняет операцию, противоположную действиям функции shift. Она последовательно добавляет список в начало массива, сдвигая остальные элементы вправо и увеличивая длину массива на размер списка.

Пример использования функции shift для извлечения элемента массива:

@array = ("one", "two", "three");

$variable1 = shift (@array);

print "$variable1/$#array";

one/1

Определение длины массива

Если определен массив с именем @array, переменная $#array содержит индекс последнего его элемента. Имя может быть любым: если массив назван, например, @phonenumbers, то индекс его последнего элемента содержится в переменной $#phonenumbers.

Например, при наличии массива

@array = (1, 2, 3);

чтобы вывести число его элементов, надо добавить единицу к переменной $#array:

@array = (1, 2, 3);

print "\@array has " . "($#array + 1) . " elements.";

@array has 3 elements.

(Единица прибавляется потому, что индексы массива отсчитываются с нуля).

Использование массива в скалярном контексте также возвращает его длину. Чтобы перевести массив в скалярный контекст, можно выполнить над ним фиктивную числовую операцию (например, прибавить ноль):

@array +0

либо, что более профессионально, использовать функцию scalar:

@array = (1, 2, 3);

print "\@array has " . scalar (@array) . " elements.";

@array has 3 elements.

либо, наконец, попросту присвоить массив скалярной переменной:

@array = (1, 2, 3);

$variable = @array;

print "\@array has $variable elements.";

@array has 3 elements.

Подсказка: Обратите внимание, что если вы присвоите скалярной переменной список, а не массив, то результатом будет не длина списка, а последний элемент списка. В этом, как и во многих других примерах, проявляется контекстная зависимость команд Perl, о которой мы уже говорили в разделе 2.

Увеличение или сужение массива

Чтобы изменить число элементов в массиве, достаточно присвоить новое значение переменной $#array, хранящей индекс последнего элемента. Вот пример такой операции:

@array = (1, 2, 3);

$#array = 10;

$array[5] = "Here is a new element! ";

print $array[5];

Here is a new element!

(все «нововведенные» элементы массива получают неопределенное значение undef).

Можно полностью очистить массив, присвоив индексу последнего элемента отрицательное число:

$#array = -1;

Слияние двух массивов

Чтобы объединить два массива, их можно присвоить третьему массиву как список. В следующем примере массивы @array1 и @array2 объединяются в массив @bigarray:

@array1 = (1, 2, 3);

@array2 = (4, 5, 6);

@bigarray = (@array1, @array2);

С новым массивом можно выполнять любые операции, допустимые для массивов:

print $bigarray[5];

6

Получение среза массива

Срез массива — это часть массива, создаваемая с помощью оператора диапазона. Он имеет формат [x..y] и соответствует массиву с элементами, имеющими индексы x, x+1, ..., и далее до y включительно.

В следующем примере с помощью этой конструкции будет создан подмассив @array2, состоящий из элементов 2 и 3 массива @array:

@array = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

@array2 = @array[2..3];

print join(", ", @array2);

3, 4

Циклы и массивы

Как уже говорилось раньше в этой главе, с помощью цикла for можно последовательно перебрать элементы массива, явно обращаясь к каждому элементу по его индексу:

@array = ("one", "two", "three");

for ($loop_ind=0; $loop_ind<=$#array; $loop_ind++) {

print $array[$loop_ind];

}

onetwothree

Можно также воспользоваться другим оператором цикла — foreach. Он непосредственно перебирает элементы массива:

@array = (1, 2, 3, 4, 5);

foreach $element (@array) {

print "$element\n";

}

1

2

3

4

5

Можно организовать цикл сразу по нескольким массивам, перечислив последние в списке (что нтерполирует элементы массивов в один список)

@array1 = (1, 2, 3);

@array2 = (4, 5, 6);

foreach $element (@array1, @array2) {

print "$element\n";

}

1

2

3

4

5

6

Вместо цикла foreach можно использовать цикл for (на самом-то деле for и foreach — это один и тот же оператор):

@array = (1, 2, 3, 4, 5);

for $element (@array) {

print "$element\n";

}

1

2

3

4

5

При желании можно даже использовать цикл for без ссылки на конкретную переменную цикла, используя специальную переменную по умолчанию $_:

@array = (1, 2, 3, 4, 5);

for (@array) {

print;

}

12345

Итак, цикл может организовываться множеством способов. Конкретный вариант зависит только от вашего вкуса и от результатов, которые должна дать программа.

Вывод массива

Если требуется вывести массив, можно передать его функции print как

@array = ("one", "two", "three");

print "Here is the array: ",@array, ".\n";

Here is the array: onetwothree.

Недостаток этого метода в том, что print рассматривает массив, как список, а потому печатает элементы один за другим, что порождает на выходе слово "onetwothree".

Более удачная идея — использовать интерполирование строки, состоящей из заключенных в двойные кавычки слов. Указав в теле строки имя массива, получим:

@array = ("one", "two", "three");

print "Here is the array: @array.\n";

Here is the array: one two three.

Perl интерполирует массив, используя по умолчанию литеру-разделитель полей (хранится в специальной переменной $,). Возможно, требуется разбить элементы массива не пробелами, а запятыми. Для этого надо присвоить символ «запятая» переменной $,, и вот что получается в результате:

@array = ("one", "two", "three");

$, = ",";

print "Here is the array: @array.\n";

Here is the array: one,two,three.

Еще лучше использовать функцию join, создав из массива строку и явно разделив элементы запятыми:

@array = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

print join(", ", @array);

1, 2, 3, 4, 5, 6, 7, 8, 9, 10

Естественно, можно использовать цикл for или foreach по всем элементам массива:

@array = ("one", "two", "three");

foreach $element (@array) {

print "Current element = $element\n";

}

Current element = one

Current element = two

Current element = three

Ну и в завершение надо заметить, что, поскольку для доступа к любому элементу массива достаточно указать его индекс, можно организовать вывод массива любым способом и в любом формате.

Сращивание (splicing) массива

Сращивание массива означает добавление новых элементов из списка, возможно, с замещением элементов, уже хранящихся в массиве. Для этой цели используется функция splice:

splice массив, смещение, длина, список

splice массив, смещение, длина

splice массив, смещение

Если в качестве параметра задан список, то splice удаляет из массива элементы, описываемые параметрами смещение и длина, и замещает их элементами списка.

В списковом контексте функция splice возвращает элементы, удаленные из массива. В скалярном контексте она возвращает последний удаленный элемент (или undef, если элементы не удалялись). При отсутствии параметра длина, splice удаляет все элементы до конца массива, начиная с элемента с индексом смещение.

Приведем несколько примеров. Сначала в массив, в котором уже имеются элементы "one" и "two", будет добавлен элемент "three":

@array = ("one", "two");

splice (@array, 2, 0, "three");

print join(", ", @array);

one, two, three

Теперь в конец старого массива будет добавлен второй массив:

@array1 = ("one", "two");

@array2 = ("three", "four");

splice (@array1, 2, 0, @array2);

print join(", ", @array1);

one, two, three, four

Наконец, во время сращивания можно удалять элементы одного из массивов. Например, элемент "zero" первого массива заменяется массивом, содержащим элементы "two", "three" и "four":

@array = ("one", "zero");

@array2 = ("two", "three", "four");

splice (@array, 1, 1, @array2);

print join(", ", @array);

one, two, three, four

Инвертирование массива

Чтобы инвертировать массив (расположить его элементы в обратном порядке), используется функция reverse:

@New = reverse @array;

Сортировка массива

Для сортировки массива применяется функция sort (для массивов она вызывается и работает точно так же, как и для списков — см. описание sort в предыдущей главе):

@new = sort {$a <=> $b} @array;

С ее помощью можно выполнять самые хитроумные сортировки — например, вот так можно сортировать массив по убыванию:

@new = sort {$b <=> $a} @array;

Создание хешей

Хеши (хеш-таблицы, хешированные таблицы) в более ранних версиях Perl назывались ассоциативными массивами, и это описывает их суть более содержательно, так как для доступа к данным вместо числового индекса используется ключ (то есть текстовая строка), которая ассоциируется со значением.

Поскольку в хешах ссылки на данные осуществляются с помощью ключей, а не чисел, доступ к элементам обычно интуитвнее проще, чем в случае массивов. Однако организация циклов для хешей может оказаться труднее, потому что к элементу хеша невозможно получить доступ с помощью числового итератора цикла.

Имена хешей начинаются с префикса %. Так создается пустая хеш-таблица:

%hash = ();

Как и в случае массивов, при работе с элементами хешей надо использовать разыменовывающий префикс $. Вот, например, как поместить в хеш новые элементы:

%hash = ();

$hash{fruit} = apple;

$hash{sandwich} = hamburger;

$hash{drink} = bubbly;

(Здесь fruit — первый ключ хеш-таблицы, и он соответствует значению apple; sandwich является вторым ключом со значением hamburger, и так далее.)

Обратите внимание, что для ключа использются фигурные ({}), а не квадратные ([]) скобки, как это было с индексами массивов.

Отдельные элементы извлекаются из хеша также с помощью ключей:

%hash = ();

$hash{fruit} = apple;

$hash{sandwich} = hamburger;

$hash{drink} = bubbly;

print $hash{sandwich};

hamburger

На самом деле нет необходимости создавать пустой хеш, чтобы начать его заполнение. Встретив обращение к несуществующему хешу, Perl создает его автоматически. (Это — шаг навстречу программистам, попытка заставить систему работать так, как вы ожидаете.) Поэтому следующий код работает не хуже предыдущего:

$hash{fruit} = apple;

$hash{sandwich} = hamburger;

$hash{drink} = bubbly;

print $hash{sandwich};

hamburger

Не забывайте, что Perl при считывании новых элементов опускает «пробельные символы». Поэтому, например, массивы с множеством элементов можно сделать более прозрачными для понимания, использовав многострочные конструкции:

@array = (

"one", "two", "three",

"four", "five", "six"

);

Точно так же можно создавать хеш-таблицы, указывая пары ключ/значение:

%hash = (

fruit , apple,

sandwich , hamburger,

drink , bubby

);

print "$hash{fruit}\n";

apple

Вместо запятой, разделяющей пары, можно использовать синоним — конструкцию =>. С этим оператором отношение ключ/значение выглядит прозрачнее, так что программисты часто записывают команду создания хеша так:

%hash = (

fruit => apple,

sandwich => hamburger,

drink => bubby

);

print "$hash{fruit}\n";

apple

Оператор => не выполняет специальных операций — это в точности то же самое, что и обычная запятая, за исключением того факта, что он вынуждает интерпретировать слово, стоящее слева, как строку. Например, команда

print "x"=>"y"=>"z";

xyz

делает то же, что и команда

print "x", "y", "z";

xyz

Не запрещается использовать ключи, содержащие пробелы. Например, можно создать элемент хеша с ключом ice cream:

$hash2{cake} = chocjlade;

$hash2{pie} = blueberry;

$hash2{’ice cream’} = pecan;

и когда потребуется использовать его:

print "$hash{’ice cream’}\n";

pecan

При создании ключей можно использовать интерполирование текстовых строк, ограниченных двойными кавычками, а также просто переменные.

$value = $hash{$key};

Хеши обеспечивают мощное средство для хранения данных, но ссылаться на элементы хеш-таблицы напрямую, с помощью числового индекса, нельзя. Это, конечно же, не значит, что нельзя организовать цикл по элементам хеша, — об этом рассказывается далее в этой главе в разделе «Циклы по элементам хеш-таблицы».

Использование хешей

После того, как вы создали хеш, можно работать с ним, обращаясь к его элементам с помощью ключей:

$value = $hash{$key};

Кроме этого, с помощью оператора присвоения легко добавить в хеш новые элементы, как это было сделано в примере из предыдущего раздела:

$hash{fruit} = apple;

$hash{sandwich} = hamburger;

$hash{drink} = bubbly;

print $hash{sandwich};

hamburger

В списковом контексте хеш интерполируется: пары ключ/значение переходят в единый список элементов. В скалярном контексте подставляется значение истина (true), если в хеше есть хотя бы одна пара ключ/значение, и ложь (false), если хеш пуст.

Добавление элементов в хеш

Чтобы добавить новый элемент (то есть пару ключ/значение) в хеш, используйте оператор присвоения. В следующем примере в хеш %hash добавляются два значения:

%hash = ();

$hash{$key} = $value;

$hash{$key2} = $value2;

Хеши можно создавать, используя присвоение списка, и точно также можно добавлять новые элементы. В следующем примере таким образом к хешу %hash добавляется новая пара ключ/значение:

%hash = (

fruit => apple,

sandwich => hamburger,

drink => bubby

);

%hash = (%hash, dressing, ’blue cheese’);

print $hash{dressing};

blue cheese

Этот пример работает, потому что оператор списка () сперва интерполирует хеш %hash в список, а затем последний расширяется еще на одну пару ключ/значение. Из-за интерполяции, которая происходит до присвоения списком, в данном случае нельзя использовать сокращенную форму с оператором +=:

%hash += (dressing, ’blue cheese’); # Это не работает

Проверка хеша на наличие элемента

Чтобы проверить, есть ли в хеше некоторый элемент, можно использовать функцию exists. Например, в следующем примере в хеше %hash ищется элемент с ключом vegetable:

$hash{fruit} = apple;

$hash{sandwich} = hamburger;

$hash{drink} = bubbly;

if (exists($hash{"vegetable"})) {

print "Element exists.";

} else {

print "Element does not exist.";

}

Element does not exist

Удаление элементов хеша

Чтобы удалить элемент из хеша, используется функция delete. Вот пример, в котором удаляется элемент хеша и затем проверяется, существует ли еще такой элемент:

$hash{fruit} = apple;

$hash{sandwich} = hamburger;

$hash{drink} = bubbly;

delete ($hash{"fruit"});

if (exists($hash{"fruit"})) {

print "Element exists.";

} else {

print "Element does not exist.";

}

Element does not exist

Циклы по хешу

Существует множество способов органицаии цикла по элементам хеша. Функция each позволяет использовать записи хеша (то есть и ключ, и значение) целиком.

Рассмотрим пример. Сначала создадим хеш-таблицу:

$hash{fruit} = apple;

$hash{sandwich} = hamburger;

$hash{drink} = bubbly;

Теперь можно извлечь из нее пары ключ/значение, используя функцию each и присвоение списком:

$hash{fruit} = apple;

$hash{sandwich} = hamburger;

$hash{drink} = bubbly;

while (($key, $value) = each(%hash)) {

print"$key => $value\n";

};

drink => bubbly

sandwich => hamburger

fruit => apple

Использование each в данном случае полезно, так как для каждого элемента хеша при одном ее вызове возвращается как ключ, так и значение.

Кстати, элементы хеша извлекаются не в том порядке, в каком они заносились. Дело в том, что Perl хранит их, используя собственный внутренний алгоритм, оптимизирующий затраты памяти и обеспечивающий легкость доступа. Сортировка элементов описывается в разделе «Сортировка хеша» далее в этой главе.

При работе с элементами хеша можно использовать цикл foreach. Например, совместно с функцией keys, возвращающей список ключей хеша, он позволяет последовательно перебрать весь хеш:

$hash{fruit} = apple;

$hash{sandwich} = hamburger;

$hash{drink} = bubbly;

foreach $key (keys %hash){

print $hash{$key} . "\n";

}

bubbly

hamburger

apple

Однако вместо keys можно сразу воспользоваться функцией values. Она возвращает список значений, хранящихся в хеше. С ней цикл, выводящий все значения хеша, принимает следующий вид:

$hash{fruit} = apple;

$hash{sandwich} = hamburger;

$hash{drink} = bubbly;

foreach $value (values %hash){

print "$value\n";

}

bubbly

hamburger

apple

Как видите, несмотря на то, что к элементам хеша нельзя получить доступ с помощью числового индекса, Perl предоставляет достаточно средств для их перебора.

Вывод хеша

Чтобы вывести содержимое хеша можно интерполировать его в строку, заключенную в двойные кавычки:

$hash{fruit} = apple;

$hash{sandwich} = hamburger;

$hash{drink} = bubbly;

print "@{[%hash]}\n";

drink bubbly sandwich hamburger fruit apple

В результате такой печати хеш переведен в списковый контекст, то есть представлен в виде пар ключ/значение, расположенных друг за другом. Функция eachв данной ситуации дает более удачное решение:

$hash{fruit} = apple;

$hash{sandwich} = hamburger;

$hash{drink} = bubbly;

while (($key, $value) = each(%hash)) {

print "$key: $value\n";

};

drink: bubbly

sandwich: hamburger

fruit: apple

Есть множество других способов организовать цикл по элементам хеша (см. раздел «Циклы по элементам хеша» ранее в этой главе).

Сортировка хеша

Для сортировки хешей используется та же функция sort, что и для сортировки списков и массивов. Вот, например, как хеш сортируется по значению ключа:

$hash{fruit} = apple;

$hash{sandwich} = hamburger;

$hash{drink} = bubbly;

foreach $key (sort keys %hash) {

print "$key => $hash{$key}\n";

}

drink => bubbly

fruit => apple

sandwich => hamburger

Точно так же вместо сортировки по ключу можно выполнить сортировку по значению:

$hash{fruit} = apple;

$hash{sandwich} = hamburger;

$hash{drink} = bubbly;

foreach $value (sort values %hash) {

print "$value\n";

}

apple

bubbly

hamburger

Слияние хешей

Для объединения двух хешей можно использовать присвоение списком. Например, допустим, есть два хеша:

$hash1{fruit} = apple;

$hash1{sandwich} = hamburger;

$hash1{drink} = bubbly;

$hash2{cake} = chocolate;

$hash2{pie} = blueberry;

$hash2{’ice cream’} = pecan;

Оба хеша могут быть объединены следующим образом:

%bighash = (%hash1, %hash2);

print $bighash{’ice cream’};

pecan

Использование хешей и массивов в присвоении списком

При присвоении списком могут использоваться хеш-таблицы и массивы. Несколько хешей или списков справа от оператора присваивания интерполируются в один список, так что не возникает никаких синтаксических проблем. Рассмотрим пример из предыдущего раздела:

$hash1{fruit} = apple;

$hash1{sandwich} = hamburger;

$hash1{drink} = bubbly;

$hash2{cake} = chocolate;

$hash2{pie} = blueberry;

$hash2{’ice cream’} = pecan;

При присвоении списком хеши объединяются в один список:

%bighash = (%hash1, %hash2);

print $bighash{’ice cream’};

pecan

Предупреждение: Когда вы выполняете присвоение списку переменных, среди которых имеется массив или хеш, нужно быть осторожным. В Perl массивы и хеш-таблицы при присваивании им новых элементов автоматически меняют размер. Поэтому при присваивании списку значений, находящемуся слева от оператора присваивания, массив или хеш в этом списке должны находится на последнем месте. В противном случае они просто захватят все элементы, которые должны быть присвоены оставшимся переменным, и те получат неопределенное значение.

Вот как происходит присвоение списку, состоящему из двух скалярных переменных и одного массива (массив обязательно должен стоять в списке переменных последним):

($var1, $var2, @array) = (1, 2, 3, 4, 5, 6, 7, 8);

print "$var1\n";

print "$var2\n";

print "@array\n";

1

2

3 4 5 6 7 8

Использование типа данных typeglobтип-глобов

Конструкции typeglob играют в Perl роль синонимов обычных переменных. (В ранних версиях они выступали в качестве аналога ссылок, но теперь в Perl появились полноценные ссылки.) Этот тип данных позволяет связать имя одной переменной (например, data) с именем другой (например, alsodata). В результате имена типа $alsodata, @alsodata и %alsodata будут ссылаться на те же объекты, что и $data, @data и %data (то есть $alsodata будет ссылаться на те же данные, что и $data, @alsodata — на те же данные, что и @data, и так далее).

Приведем пример. Сначала создаются переменные $data и @data:

$data = "Here is the data.";

@data = (1, 2, 3);

Теперь имени data присваивается синоним alsodata:

$data = "Here is the data.";

@data = (1, 2, 3);

*alsodata = *data;

А теперь вместо имени data можно использовать имя alsodata:

$data = "Here is the data.";

@data = (1, 2, 3);

*alsodata = *data;

print "$alsodata\n";

print @alsodata;

Here is the data.

123

На самом деле происходит вот что: запись таблицы символов, обозначаемая как *имя, хранит информацию обо всех переменных с общим именем имя, —например, запись *data текущей таблицы символов хранит информацию о переменных $data, @data, %data, и так далее. В частности, для каждого типа данных записывается адрес области памяти, где это данное хранится, или ссылка на нее (то есть, ссылка на скалярную переменную $data, ссылка на массив @data, ссылка на хеш-таблицу %data, и т. д.).

При присвоении конструкций typeglobтип-глобов Perl копирует записи таблицы символов для одного имени в другое имя. Символ *, используемый в конструкции typeglob, можно считать шаблоном любого префикса типа данных ($, %, и так далее). Более подробно процесс копирования записей таблицы символов будет рассмотрен в следующем разделе.

Подсказка: Чтобы после операции *alsodata = *data переменная $alsodata стала синонимом $data, на момент присвоения переменная $data может и не быть определена. Поскольку Perl компилирует код непосредственно перед выполнением сценария, память для $data будет выделена заранее и ссылка на нее уже попадет в структуру *data. Иначе вместо ссылки на переменную $data в запись таблицы символов *alsodata была бы скопирована информация о том, что такой скалярной переменной еще нет, и переменные $data и $alsodata не могли бы ссылаться на одно и то же значение.

При использовании конструкций typeglob необязательно копировать всю запись таблицы символов, относящуюся к данному имени. Если справа от оператора присваивания стоит ссылка только на один тип данных (например, скаляр), то и новое имя и выступать синонимом только для указанного типа:

$data = "Here is the data.";

@data = (1, 2, 3);

*alsodata=\$data; # Введем синоним только для скаляров

В данном случае $alsodata становится синонимом $data, но @alsodata не будет синонимом для @data, %alsodata — для %data, и т. д. Иными словами, такая команда сработает нормально:

print "$alsodata\n";

Here is the data.

а такая работать не будет:

print @alsodata;

Тип данных typeglob и записи в таблице символов

Как уже говорилось, Perl хранит информацию о переменных во внутренней таблице имен (таблице символов) в виде отдельных записей. Каждая запись таблицы символов представляет собой тип typeglob. Этот тип данных можно считать небольшим хешем, значениями которого являются ссылки на область памяти, где хранятся соответствующие переменным данные.

Ключами для таких хешей (хотя этой информации вы, возможно, не найдете в документации Perl) выступают имена типов данных, записанные заглавными буквами (то есть ARRAY, HASH, и так далее). Это может быть полезным, если требуется непосредственный доступ к таблице символов Perl. Допустим, в программе определена переменная со значением 5:

$variable = 5;

В этом случае *variable — имя переменной типа typeglob, хранящей информацию о $variable, а *variable{SCALAR} — ссылка на значение переменной $variable. Чтобы получить значение переменной $variable через ссылку на область данных в таблице символов, использется разыменовывающий оператор $:

$variable = 5;

print ${*variable{SCALAR}};

5

Глава 4. Операторы и приоритеты операторов

Коротко 

В трех предыдущих главах обсуждались стандартные форматы данных в языке Perl. В этой главе речь идет о работе с данными с помощью операторов. Операторы позволяют совершать операции с данными, даже если это простое сложение:

print 2 + 2;

4 

Операторы могут выполнять и более сложные вычисления, подобно тернарному («тройственному») условному оператору, рассматриваемому в следующем примере:

while (<>)

{ print $_ < 10 ? $_ : "${\((a,b,c,d,e,f)[$_ - 10])}\n"; }

Это выражение считывает из входных данных числа от 0 до 15 и выводит шестнадцатеричную цифру (если вы не знакомы с оператором ?:, обратитесь к разделу «Тернарный условный оператор» в этой главе).

Операторы Perl выполняют самые различные действия, но в первом приближении они могут быть разбиты на унарные, бинарные, тернарные и списковые. Унарным операторам требуется один операнд — например, в случае оператора !, выполняющего побитное логическое отрицание , команда $notvariable = !$variable осуществит логическое инвертирование содержимого переменной $variable и занесет результат в переменную $notvariable. Бинарные операторы требуют двух операндов — например, для оператора сложения + команда $sum = 2 + 2 вычислит сумму двух целых чисел и занесет ее в переменную $sum. Тернарные операторы используют три операнда — например, в результате работы условного оператора ?: команда

$absvariable = $variable >= 0 ? $variable : -$variable;

найдет и занесет в переменную $absvariable абсолютное значение выражения $variable. Наконец, списковые операторы , подобно оператору print, используют списки операндов (как, например, команда print 1, 2, 3, 4, 5).

Глава 4. Операторы и приоритеты операторов

Ссылка на функцию print как на оператор на первый взгляд может показаться странной. Но в Perl функция с аргументами, указанными без круглых скобок, рассматривается в качестве оператора. Более того, при вычислении выражений используются правила приоритета, определенные для операторов (более подробно об этом говорится в разделе «Наивысший приоритет: термы и списки, стоящие справа» этой главы). Прежде чем переходить к работе с операторами, необходимо разобраться с понятием сравнительного приоритета.

Приоритет операторов

Операторы Perl по мере убывания приоритета (первая строчка соответствует наивысшему приоритету) перечислены в табл. 4.1.

Время от времени при работе с Perl приходится учитывать сравнительный приоритет операторов. В одном выражении может использоваться несколько операторов:

print 2 + 3 * 4;

Сложит ли Perl 2 и 3 перед умножением на 4 или сперва умножит 3 на 4 и лишь затем прибавит 2? Этот вопрос проясняется при знании правил приоритета операторов, существующих в Perl. Как видно из табл. 4.1, умножение (*) имеет более высокий приоритет, чем сложение (+). Поэтому Perl сначала умножит 3 на 4, а потом прибавит 2:

print 2 + 3 * 4;

14 

Естественно, можно изменить порядок выполнения операций, расставив скобки:

print ((2 + 3) * 4);

20 

Кстати, обратите внимание, что эту команду нельзя задать как

print (2 + 3) * 4;

Поскольку print работает либо как оператор, либо как функция, скобка указывает Perl, что print надо использовать в качестве функции, а 2 + 3 — это параметр, передаваемый ей. Поэтому Perl послушно выведет:

print (2 + 3) * 4;

5 

Однако эту проблему можно обойти. Если интерпретация списочного оператора (того же print) с аргументом, начинающимся со скобки, как вызова функции, не требуется, то перед скобками можно поставить унарный оператор +. Он не окажет никакого воздействия на само выражение, однако сообщит Perl, что интерпретировать конструкцию в скобках как вызов функции не следует:

print +(2 + 3) * 4;

20 

Итак, сравнительный приоритет операторов весьма важен. Разделы этой главы будут организованы в соответствии с табл. 4.1, то есть операторы с наивысшим приоритетом обсуждаются в первую очередь.

Приоритет операторов

Таблица 4.1. Сравнительный приоритет операторов (операторы, перечисленные
в одной группе, имеют одинаковый приоритет)

Оператор Ассоциативность 

термы Левая
правый оператор списка

-> Левая

++ Не определена
--

** Правая

! Правая
~
\
унарный +
унарный _

=~ Левая
!~

* Левая
/
%
x

+ Левая
_
.

<< Левая
>>

именованные унарные Не определена
операторы,
операторы проверки файлов

<> Не определена
<=
>=
lt
gt
le
ge

== Не определена
!=
<=>
eq
ne
cmp

& Левая

| Левая
^

&& Левая

|| Левая

продолжение И

Таблица 4.1 (продолжение )

Оператор Ассоциативность 

.. Не определена
...

?: Правая

= Правая
+=
-=
*=
/=
%=
&=
|=
^=
.=
x=
**=
<<=
>>=
&&=
||=

, Левая
=>

левый оператор списка Не определена

not Правая

and Левая

or Левая
xor

Непосредственные решения 

Наивысший приоритет: термы и списки, стоящие справа

Термы имеют в Perl наивысший приоритет. К термам относятся переменные, выражения в кавычках, выражения в скобках, конструкции do и eval, безымянные (анонимные) массивы и хэши, создаваемые с помощью конструкций [] и {} (см. главу 8), а также функции с аргументами, заключенными в скобки.

Точно так же наивысший приоритет, направленный влево (leftward precedence), имеет оператор списка (набор элементов, разделенных запятой). Это значит, что по сравнению с операторами, находящимися слева от него, он имеет наивысший приоритет, а когда его приоритет сравнивается с операторами, стоящими справа, этот же оператор списка получит гораздо более слабый правый приоритет (rightward precedence).

Рассмотрим следующий пример:

print 1, 2, 3, 4, sort 9, 8, 7, 6, 5;

123456789 

Как видно из результата, сперва оператор sort сортирует список, указанный в качестве его аргумента, а затем print выводит список, являющийся объединением двух списков. Когда идет анализ числа 9, то слева от него оказывается оператор sort, а справа — оператор списка. Так как по отношению к левой стороне у оператора списка больший приоритет, то Perl сперва объединит число 9 в один список с последующими элементами (см. также раздел «Списки, стоящие слева» ближе к концу главы) и лишь затем применит к результату операцию сортировки. С другой стороны, когда Perl доходит до числа 4, то имеет дело с двумя идущими подряд операторами — оператором списка и оператором sort. Оператор списка имеет более слабый приоритет по отношению к расположенному справа от него оператору sort, поэтому объединение элементов в список будет отложено до тех пор, пока оператор сортировки не выполнит свою работу.

Оператор-стрелка

Инфиксный оператор ->, разыменующий ссылку, заимствован из языка С. Если справа от него находится конструкция [...] или {...}, то слева должна находиться ссылка на массив или хэш. В следующем примере создается ссылка на хэш (с помощью оператора \, возвращающего адрес переменной), а затем с помощью оператора -> обеспечивается доступ к содержимому хэша через полученную ссылку:

$hash{fruit} = apple;

$hash{sandwich} = hamburger;

$hash{drink} = bubbly;

$hashref = \%hash;

print $hashref -> {sandwich}; 

hamburger 

Когда справа от оператора нет квадратных [...] или фигурных {...} скобок, а левая часть не представляет собой ссылки на массив или хэш, то слева должен находиться либо объект, либо имя класса, а справа — его метод:

$result = $myobject -> mymethod{$data);

(Более подробно об объектах и классах рассказывается в главе 16.)

Автоприращение и автоуменьшение

Оператор приращения (++) и оператор уменьшения (__) в Perl работают точно так же, как в языке С. Находясь перед именем переменной, они увеличивают или уменьшают ее значение на единицу до того, как подставить значение в выражение. Когда эти операторы оказываются после имени переменной, они сперва подставляют в выражение значение переменной, а потом увеличивают или уменьшают ее содержимое.

Допустим, определены переменные $variable1 и $variable2:

$variable1 = 1;

$variable2 = 1;

Увеличить значение переменной можно, применив оператор ++ в качестве префикса:

print ++$variable1 . "\n";

print $variable1 . "\n";

2

2 

Если тот же оператор использовать в качестве суффикса, значение переменной изменится лишь после того, как оператор вернет ее текущее значение:

print $variable2++ . "\n";

print $variable2 . "\n";

1

2 

Отметим, что оператор работает также и с текстовыми строками, хранящимися в скалярных переменных (однако только когда скалярная переменная не используется в числовом контексте). Например, программа

$variable = 'AAA';

print ++$variable . "\n";

$variable = 'bbb';

print ++$variable . "\n";

$variable = 'zzz';

print ++$variable . "\n";

выводит результат:

AAB

bbc

aaaa 

Оператор связывания

Возведение в степень

Оператор возведения в степень выглядит как **. Это бинарный оператор, который возводит первый аргумент в степень, указанную вторым аргументом. Например:

print 2 ** 10'

1024 

Унарные операторы

В Perl есть четыре унарных оператора:

· ! — логическое отрицание (логическая операция not).

· _ — арифметическое отрицание (унарный минус).

· + — пустая арифметическая операция (унарный плюс).

· ~ — побитное отрицание (побитное дополнение до единицы).

· \ — создание ссылки (вычисление адреса объекта).

Например, логическое отрицание числа ноль есть единица:

print !0;

1 

Побитное же отрицание инвертирует все биты в числе. В частности, операция ~0 позволит определить максимальное, представимое в конкретной системе беззнаковое целое (она установит все двоичные разряды слова, выделенного для числа, в единицы):

print ~0;

4294967295 

Отсюда следует, что слово на данной машине имеет 32 разряда, так как 4294967295 = 2**32 _ 1.

Оператор связывания

Оператор связывания =~ связывает скалярную переменную и операцию поиска/замены по шаблону (более подробно эти операции рассматриваются в главе 6). Строковые операторы s/.../.../, m/.../, tr/.../ по умолчанию работают с переменной $_. Оператор связывания =~ используется, чтобы они работали с конкретной скалярной переменной. Например, поместим строку в переменную $line:

$line = ".Hello!";

Теперь выполним поиск по шаблону:

$line = ".Hello!";

if ($line =~ m/^\./)

{ print "Should't start a sentence with a period!"; }

Should't start a sentence with a period! 

Оператор !~ работает точно так же, за тем исключением, что он выполняет логическое отрицание над результатом, возвращаемым оператором =~ (как и другие функции, операции поиска и замены возвращают некоторое значение в качестве результата своей работы — более подробно об этом рассказывается в главе 6).

Умножение и деление

Оператор умножения * перемножает два числа:

print 2 * 4;

8 

Оператор деления / делит одно число на другое:

print 16 / 3;

5.33333333333333 

Оператор % вычисляет остаток от целочисленного деления двух значений:

print 16 % 3;

1

Оператор повторения x дублирует действие заданное количество раз. В скалярном контексте он возвращает строку, полученную из значения, стоящего слева от оператора, путем повторения этого значения (конкатенации с собой) столько раз, сколько задано числом справа от оператора. Например, вот как напечатать строку из 30 дефисов:

print '-` x 30;

------------------------------ 

Если левый операнд является списком, заключенным в круглые скобки, то оператор повторения продублирует список необходимое число раз. Вот как, например, создать список из восьмидесяти единиц и присвоить его массиву:

@ones = (1) x 80;

Следующий неочевидный пример заменяет все единицы на пятерки:

@ones = (5) x @ones;

(поскольку правый операнд оператора повторения должен быть числом, то массив @ones интерпретируется в скалярном контексте, то есть как целое число, равное текущему количеству элементов массива).

Сложение, вычитание и конкатенация

Оператор сложения (+) складывает два числа:

print 2 + 2;

4 

Оператор вычитания (_) вычитает одно число из другого:

print 4 - 2;

2 

Операторы проверки файлов

Оператор конкатенации (.) объединяет две строки:

print "Hello " . "there.";

Hello "there. 

Все эти операторы являются бинарными (в частности, не надо путать бинарные операторы сложения и вычитания с унарным плюсом и унарным минусом).

Оператор сдвига

Оператор сдвига влево << возвращает значение левого аргумента, сдвинутого влево на число битов, определенное правым аргументом:

print 2 << 10;

2048 

Оператор сдвига вправо >> возвращает значение левого аргумента, сдвинутого вправо на число битов, определенное правым аргументом. Например:

print 2048 >> 3;

256 

Именованные унарные операторы

Perl рассматривает функции с одним скалярным аргументом (не списком), как именованные унарные операторы, если аргумент не заключен в круглые скобки. (Такими функциями являются, к примеру, sqrt, defined, eval, return, chdir, rmdir, oct, hex, undef, exists и многие другие.) Вот, например, как используется в качестве оператора функция sqrt:

print sqrt 4;

2 

Операторы проверки файлов

Perl поддерживает массу операторов для проверки состояния файлов, что позволяет полностью контролировать файлы и их дескрипторы (см. главу 12 относительно работы с файлами в Perl). Вот как записываются эти операторы (X заменяет любой оператор):

-X дескриптор-файла 

-X выражение 

-X

Вместо X надо подставить один из операторов, приведенных в табл. 4.21. Возможно, какая-то информация из этой таблицы окажется для вас незнакомой, если только вы не работаете с операционной системой Unix. Так, аббревиатуры UID

1 В документации, сопровождающей Perl (файл perlfunc), можно найти и другие операторы, не включенные в табл. 4.2. По большей части их работа сильно зависит от системы (в частности, в Windows многие просто бесполезны, а многие работают неправильно для FAT16 и FAT32). — Примеч. ред. 

и GID означают соответственно идентификатор пользователя (user ID) и группы (group ID). Если опущен аргумент, используется переменная $_ (за исключением оператора -t, который по умолчанию работает с входным потоком данных STDIN).

Вот несколько примеров проверки файлового дескриптора STDIN (по умолчанию соответствует входному потоку данных):

print -e STDIN; # Существует ли STDIN?

1 

print -t STDIN; # Присоеденен ли он к терминалу?

1 

print -z STDIN; # Имеет ли он нулевой размер?

1 

(Обратите внимание, что логическое значение истина (true) возвращается этими операторами, как число один.)

Таблица 4.2. Операторы тестирования файлов

Оператор Проверяемая информация 

-A Время, прошедшее с момента последнего
обращения к файлу

-b Блочный файл

-B Двоичный файл

-c Символьный файл

-C Время, прошедшее с момента последнего
изменения индексного дескриптора

-d Каталог

-e Файл существует

-f Обычный файл

-g Установлен атрибут SETGID

-k Установлен атрибут Sticky bit1 

-l Файл является символической ссылкой

-M Время существования файла в днях на момент
запуска сценария

-o Файл принадлежит текущему пользователю
(effective UID)

-O Файл принадлежит реальному пользователю
(real UID)

-p Файл является именованным каналом (FIFO)

-r Файл доступен для чтения текущему пользователю
(effective UID) или группе (effective GID)

-R Файл доступен на чтение реальному пользователю
(real UID) или группе (real GID)

1 «Липкий бит» устанавливается для каталога и запрещает пользователю, имеющему право на запись в этот каталог, удалять файлы, владельцем которых он не является. Для пользователей системы Unix это понятие знакомо и без перевода, а в операционной системе Windows ничего подобного просто нет. — Примеч. ред. 

Операторы сравнения

Оператор Проверяемая информация 

-s Размер файла (он же — проверка файла на нулевой размер)

-S Файл является сокетом (сетевым соединением)

-t Файл открыт на текущем терминале

-T Текстовый файл

-u Для файла установлен атрибут SETUID

-w Файл доступен для записи текущему пользователю
(effective UID) или группе (effective GID)

-W Файл доступен для чтения реальному
пользователю (real UID) или группе (real GID)

-x Файл доступен для выполнения текущему пользователю
(effective UID) или группе (effective GID)

-X Файл доступен для выполнения реальному
пользователю (real UID) или группе (real GID)

-z Файл имеет нулевой размер

Операторы сравнения

Операторы сравнения — это бинарные операторы, которые сравнивают данные, возвращая единицу в качестве значения истина (true) и пустую строку в качестве значения ложь (false). Они перечислены в табл. 4.3. Обратите внимание, что одни операторы используются для сравнения чисел, а другие — для сравнения строк.

Подсказка. Обратите внимание, что оператор «больше или равно» обозначен как >=, чтобы не спутать его с оператором => — синонимом запятой (разделителя элементов списка). 

В следующем примере проверяются числа, вводимые пользователем, и, если введенное число оказывается больше ста, выдается сообщение об ошибке:

while (<>) {

if ($_ > 100)

{ print "Too big!\n"; }

}

Вы можете использовать логические операторы && и || или их сородичей and и or (отличающихся меньшим приоритетом) для объединения отдельных логических утверждений. В следующем примере задается требование того, чтобы введенная пользователем буква была между k и m:

print "Please enter letters from k to m\n";

while (<>) {

chop;

if ($_ lt 'k' or $_ gt 'm') {

print "Please enter letters from k to m\n";

} else

{ print "Thank you - let's have another!\n"; }

}

Таблица 4.3. Операторы сравнения

Оператор Тип данных Возвращаемое значение 

< Число Истина, если левый операнд меньше правого

> Число Истина, если левый операнд больше правого

<= Число Истина, если левый операнд меньше или равен правому

>= Число Истина, если левый операнд больше или равен правому

lt Строка Истина, если левый операнд меньше правого

gt Строка Истина, если левый операнд больше правого

le Строка Истина, если левый операнд меньше или равен правому

ge Строка Истина, если левый операнд больше или равен правому

Операторы равенства

Кроме рассмотренных в предыдущем разделе операторов сравнения Perl поддерживает операторы проверки на равенство, приведенные в табл. 4.4. (Поскольку они имеют меньший приоритет, чем операторы сравнения, для них отведен особый раздел.) Как и в случае операторов сравнения, существуют отдельные операторы для числовых данных и текстовых строк.

В следующем примере пользователь должен ввести букву y (игрек). Запрос повторяется до бесконечности с выводом сообщения об ошибке, пока пользователь не выполнит требуемое действие:

print "Please type the letter y\n";

while (<>) {

chop;

if ($_ ne 'y') {

print "Please type the letter y\n";

} else {

print "Do you always do what you're told?\n";

exit;

}

}

В результате работы этой программы на выходе может получиться следующий текст:

Please type the letter y

a

Please type the letter y

b

Please type the letter y

c

Please type the letter y

y

Do you always do what you're told? 

Побитное «ИЛИ»

Таблица 4.4. Операторы сравнения

Оператор Тип данных Возвращаемое значение 

== Число Истина, если левый операнд равен правому

!= Число Истина, если левый операнд не равен правому

<=> Число _1, 0 или 1 в зависимости от того, является ли
левый операнд меньше правого, равным правому
или больше правого

eq Строка Истина, если левый операнд равен правому

ne Строка Истина, если левый операнд не равен правому

cmp Строка _1, 0 или 1 в зависимости от того, является ли левый операнд
меньше правого, равным правому или больше правого

Побитное «И»

Оператор побитного логического И (&) возвращает результат логического умножения and над битами операндов, выровненных друг относительно друга. (Таблица логического умножения битов приведена в табл. 4.5.)

Например, выполнение этой операции над числами 5 (биты 0 и 2 равны единице) и 4 (только бит 2 равен единице) дает результат 4:

print 5 & 4;

4 

Таблица 4.5. Оператор «И»

and 0 1

0 0 0

1 0 1

Побитное «ИЛИ»

Оператор побитного логического ИЛИ (|) возвращает результат логического сложения or над битами операндов, выровненных друг относительно друга. (Таблица логического сложения битов приведена в табл. 4.6.)

Таблица 4.6. Оператор «ИЛИ»

or 0 1

0 0 1

1 1 1

Например, выполнение этой операции над числами 4 (бит 2 равен единице) и 1 (бит 0 равен единице) дает результат 5 (где единице равны биты 0 и 2):

print 4 | 1;

5 

Побитное «Исключающее ИЛИ»

Оператор побитного логического ИСКЛЮЧАЮЩЕГО ИЛИ (^) возвращает результат логической операции xor над битами операндов, выровненных друг относительно друга. (Таблица операции xor для битов приведена в табл. 4.7.)

Обратите внимание, что для «исключающего или», если и в первом, и во втором операнде бит равен единице, на выходе получается ноль. Несколько примеров:

print 0 ^ 0;

0 

print 1 ^ 0;

1 

print 1 ^ 1;

0 

print 0 ^ 1;

1 

print 5 ^ 4;

1 

Таблица 4.7. Оператор «Исключающее ИЛИ»

xor 0 1

0 0 0

1 1 0

Логическое «И» в стиле языка С

Оператор && выполняет логическую операцию И над двумя логическими значениями. Он может объединять два оператора сравнения, проверяя, чтобы оба возвращали значение истина перед тем, как вернуть это значение в качестве результата. Пример:

print "Please enter numbers from 5 to 10\n";

while (<>) {

chop;

if ($_ >= 5 && $_ <= 10) {

print "Thank you - let's have another!\n";

} else

{ print "Please enter numbers from 5 to 10\n"; }

}

Речь идет об «операторе в стиле С», поскольку в данном случае используется тот же самый синтаксис, а кроме того, этот оператор имеет такой же приоритет, как и в C. (В Perl существует и еще один логический оператор аналогичного действия, обозначаемый как and, но его приоритет меньше.) Этот оператор также известен как оператор короткого действия — если левый операнд соответствует условию ложь, то второй операнд не вычисляется и не проверяется.

Логическое «ИЛИ» в стиле языка С

В отличие от С в Perl этот оператор возвращает не ноль или единицу, а последнее вычисленное значение, то есть первый операнд, если он соответствует условию ложь, или второй, если первый соответствует условию истина (напомним, что в Perl условию ложь соответствуют любые пустые значения — пустая строка, неопределенная переменная, undef и т. д., а условию истина — все остальные).

Логическое «ИЛИ» в стиле языка С

Оператор || выполняет логическую операцию ИЛИ над двумя логическими значениями. Он может объединять два оператора сравнения, возвращая значение истина, если хотя бы один из операндов возвращает значение истина. В этом примере печатается сообщение об ошибке, если введенное пользователем число не входит в указанный диапазон:

print "Please enter numbers from 5 to 10\n";

while (<>) {

chop;

if ($_ < 5 || $_ > 10) {

print "Please enter numbers from 5 to 10\n";

} else

{

print "Thank you - let's have another!\n";

}

}

Опять-таки, мы говорим о «стиле С», поскольку в данном случае используется аналогичный синтаксис и аналогичный приоритет. (В Perl есть и другой логический оператор аналогичного действия, обозначаемый как or, но его приоритет меньше.) Как и предыдущий оператор &&, он является оператором короткого действия — если левый операнд соответствует условию истина, то второй операнд не вычисляется и не проверяется.

В отличие от С в Perl этот оператор возвращает не ноль или единицу, а последнее вычисленное значение, то есть первый операнд, если он соответствует условию истина, или второй, если первый соответствует условию ложь (напомним, что в Perl условию ложь соответствуют любые пустые значения — пустая строка, неопределенная переменная, undef и т. д., а условию истина — все остальные).

Тот факт, что оператор || возвращает последнее вычисленное значение, а не значения истина или ложь (именно в таком стиле о нем и надлежит думать при написании программы), позволяет использовать его для самых разных операций. Например, можно испытать несколько вариантов действия и в случае окончатель ной неудачи вывести сообщение об ошибке, прекратив работу:

$result = this($data) || that($data)

|| die "Can't get this() or that() to work\n";

Здесь нормальная последовательность действий прерывается с помощью функции die. Она вызовет выход из программы с выводом сообщения вида «Can't get this() or that() to work at try.pl line X».

Оператор диапазона в списковом контексте

В зависимости от контекста возможны два принципиально разных варианта работы оператора диапазона (..). В списковом контексте он возвращает массив значений с шагом 1, лежащих в диапазоне, указанных левым и правым операндами, начиная со значения, указанного левым операндом. Например, следующая команда выводит одну и ту же строку пять раз подряд:

for (1 .. 5)

{ print "Here we are again!\n"; }

Here we are again!

Here we are again!

Here we are again!

Here we are again!

Here we are again! 

То же самое произойдет, если немного уменьшить верхнюю границу диапазона:

for (1 .. 4.5)

{ print "Here we are again!\n"; }

Here we are again!

Here we are again!

Here we are again!

Here we are again!

Here we are again! 

(Если верхняя граница не совпадает с последним сгенерированным значением, то оператор диапазона возвращает на один элемент больше — на последнем шаге он включает в список значение, удовлетворяющее условию «больше или равно».)

Необходимо отметить, что в Perl версии 5 для операторов for и foreach подобная конструкция не приводит к выделению промежуточного временного массива. Напротив, в старых версиях Perl при выполнении цикла

for (1 .. 1_000_000_000) {

# код тела цикла

}

вы можете столкнуться с серьезными проблемами нехватки памяти.

В списковом контексте оператор диапазона работает не только с числами, но и со строками (подобно рассмотренным ранее операторам ++ и --). Например, можно использовать команду

@alphabet = ('A' .. 'Z');

чтобы получить список заглавных латинских букв, или команду

@hexdigit = ('0' .. '9', 'a' .. 'f')[$num & 15];

для получения шестнадцатеричной цифры, или команду

@date = ('00' .. '31');

print $date[$day];

чтобы вывести дату с ведущими нулями.

Оператор диапазона в скалярном контексте

Оператор диапазона в скалярном контексте

В скалярном контексте оператор диапазона возвращает логическое значение, соответствующее условию ложь или истина. В отличие от рассмотренных ранее операторов сравнения, каждый оператор диапазона, используемый программой на Perl в скалярном контексте, ведет себя как маленький триггер — у него есть собственное внутреннее состояние, которое учитывается при очередном выполнении данного фрагмента кода.

Рассмотрим, как работает оператор диапазона в скалярном контексте. Начальное состояние оператора соответствует условию ложь. Оно таким и останется, если при очередном вычислении оператора левый операнд соответствует условию ложь, причем значение, вырабатываемое оператором диапазона, будет также соответствовать условию ложь. Если же при очередной проверке левый операнд обратился в истину, внутреннее состояние оператора диапазона станет истиной — соответственно возвращаемое оператором значение также будет истиной. Оператор будет оставаться в таком состоянии до тех пор, пока в истину не обратится правый операнд. Как только это произойдет, внутреннее состояние оператора немедленно обратится снова в ложь, однако вы это увидите только при следующем обращении к оператору — последнее выданное оператором диапазона значение будет истина. Очередное выполнение оператора диапазона приведет к проверке левого операнда, и весь цикл может повториться сначала.

Оператор диапазона проверяет только левый операнд, если внутреннее состояние оператора соответствует условию ложь, и только правый операнд, если его внутреннее состояние соответствует значению истина. При переключении внутренне го состояния оператора из истины в ложь проверка левого операнда откладывает ся до следующего обращения к оператору, а в качестве выходного значения оператора возвращается истина. При переключении же из состояния ложь в состояние истина оператор диапазона обычно тут же проверяет свой правый операнд. Если результат оказался истиной, внутреннее состояние переключается обратно в ложь, однако значение ложь опять-таки может быть выдано в качестве выходного значения только при следующем обращении к данному участку кода, а текущим выходным значением будет истина. Если же вы хотите при переключении из состояния ложь в состояние истина отложить проверку правого операнда до следующего обращения к оператору диапазона, используйте три точки ... вместо двух. (Первый вариант поведения характерен для awk, второй — для sed.)

Однако часто вместо правого и левого операндов оператора диапазона указываются отнюдь не логические условия, а числовые или строковые константы. Если левый или правый операнды оказываются константой, то вместо вычисления соответству ющего операнда («проверки условия») специальная переменная Perl $. (она хранит текущий номер считанной из файла строки для файла, к которому было последнее обращение) сравнивается с константой по признаку «больше или равно».

Такое странное поведение оператора диапазона в скалярном контексте связано с тем, что он моделирует операторы, которые в awk и sed проверяют номер строки, считанной из файла. Например, чтобы напечатать лишь часть строк из входного файла, используется команда:

while (<>)

{ if (101..200) {print;} # Печать строк 101-200 }

Чтобы понять работу оператора диапазона в скалярном контексте, рекомендует ся внимательно разобраться с несколькими образцами кода, написанного профессиональными программистами. Некоторые из примеров приведены в документа ции Perl, а также могут быть взяты из его библиотечных модулей.

Подсказка. В качестве условия ложь оператор диапазона возвращает пустую строку, в качестве условия истина — количество успешных обращений к данному оператору диапазона. При первом успешном обращении это число равно единице. Оно сбрасывается обратно в единицу в случае перехода к следующему оператору диапазона или при возникновении условия ложь. Чтобы особым образом обработать первый элемент, для которого оператор диапазона вернул значение истина, достаточно проверить, не равно ли это значение единице. Чтобы особым образом обработать последний элемент, можно использовать следующее свойство Perl: если оператор диапазона вызывается последний раз (то есть в момент перехода в состояние ложь), возвращаемое им значение-счетчик содержит суффикс «Е0» (значение, возвращаемое оператором диапазона, является строкой, а не числом, но, как уже отмечалось, Perl не делает большой разницы между числами и строками, преобразуя их из одного формата в другой по мере необходимости). Этот суффикс, характерный для числа с плавающей точкой, не влияет на числовое значение строки-счетчика, однако может служить индикатором, что достигнут последний элемент. 

Тернарный условный оператор

Условный оператор ?: требует трех операндов. Его работа подобна конструкции if-then-else. Если операнд, указанный перед вопросительным знаком, соответству ет условию истина, то вычисляется и возвращается в качестве значения второй операнд, расположенный между вопросительным знаком и двоеточием. Если же значение первого операнда соответствует условию ложь, то вычисляется и возвращается третий операнд.

В следующем примере условный оператор вычисляет абсолютное значение числа, вводимого пользователем (предположим, что встроенной функции abs не существует):

while (<>)

{ print $_ >= 0 ? $_ : -$_ }

Вводимое число сравнивается с нулем. Если число неотрицательное (больше или равно нулю), то программа выводит число без изменений. В противном случае используется унарный минус, который инвертирует знак числа перед выводом.

В следующем примере числа из диапазона 0_15, вводимые пользователем, преобразуются в шестнадцатеричный формат:

while (<>) {

print $_ < 10 ? $_ :

"{\((a, b, c, d, e, f)[$_ - 10])}\n";

}

Здесь нет проверки на ошибки. Можно составить более аккуратную программу с вложенными операторами ?:, которая проверяет вводимое значение и выводит сообщение об ошибке, если введенное число не может быть преобразовано к шестнадцатеричному числу, состоящему из одной цифры:

Оператор-запятая

while (<>) {

print $_ > 0 && $_ < 10 ? $_ :

"{\($_ < 16 ? (a, b, c, d, e, f)[$_ - 10] :

\"Number is not a single hex digit.\"

)}\n";

}

(Чтобы разобраться, как работает этот пример, требуется материал, который рассматривается позднее.)

Оператор присвоения

Оператор присвоения заносит данные, указанные в качестве правого операнда, по месту, указанному левым операндом. В качестве левого операнда должны выступать так называемые «левые значения» (обычно это переменные, более подробно см. главу 2, раздел «Что такое "левое значение"?»), в качестве правого операнда — любое вычисляемое выражение:

$variable = 5;

Подобно языку С, в Perl можно использовать сокращенные формы оператора присваивания. Например, комбинирование присвоения с умножением записывается следующим образом:

$doubleme *= 2;

В этом примере содержимое переменной $doubleme умножается на два, а результат помещается в ту же самую переменную. Список разрешенных «коротких» операторов присвоения выглядит как

**= += *= &= <<= &&=\ -= /= |= >>= ||= .= %= ^= x=

В отличие от С в Perl оператор присвоения представляет собой полноценное «левое значение», которое в случае необходимости можно использовать как переменную. Например, в следующем случае мы отсекаем последний символ именно от переменной $input, а не от выражения, возвращаемого оператором присваивания:

chop($input = 123);

print $input;

12 

Это свойство помогает создавать немного более компактный код. Например, следующий фрагмент программы считывает строку из входного потока, удаляет последний символ (конец строки) и оставляет результат в переменной $input — и все в одну строчку:

chop($input = <>);

Оператор-запятая

Этот оператор работает различным образом в скалярном и списковом контексте. В скалярном контексте он вычисляет свой левый аргумент, отбрасывает получен

ный результат, затем вычисляет правый аргумент и возвращает его в качестве значения. Например:

$variable = (3, 4, 5);

print $variable;

5 

В списковом контексте оператор-запятая выступает в качестве разделителя списка, помещая в список оба аргумента, как в следующем примере:

@array = (3, 4, 5);

print join(", ", @array);

3, 4, 5 

Конструкция => является синонимом для оператора-запятой. Начиная с версии Perl 5.001 этот оператор заставляет интерпретировать аргумент, указанный слева, как текстовую строку.

Подсказка. Символ => является диграфом. Диграф — это символ, составленный из двух идущих подряд литер. 

Списки, стоящие слева

Оператор списка (набор элементов, разделенных запятой) имеет различный приоритет, когда сравнивается с операторами, стоящими слева и справа. Относитель но операторов, стоящих справа, приоритет у оператора-списка меньше, чем для операторов, стоящих слева. Например, тот факт, что оператор-запятая на одну ступень более приоритетен, чем правый оператор списка, позволяет завершить список до того, как подать его на вход предшествующего списочного оператора. Вот пример, уже использовавшийся ранее:

print 1, 2, 3, 4, sort 9, 8, 7, 6, 5;

123456789 

Рассмотрим, например, запятую, стоящую после числа 7. Список из трех чисел 9, 8 и 7, стоящий слева от нее, имеет более слабый приоритет, чем запятая, и поэтому вместо того, чтобы немедленно «скормить» имеющийся список оператору sort, будет продолжено пополнение списка новым элементом 6.

Логическое NOT

Оператор not возвращает логическое отрицание своего операнда. Его действие аналогично оператору !, рассмотренному ранее, за тем исключением, что приоритет этого оператора значительно ниже (таким образом, можно не беспокоиться о круглых скобках в выражениях, разделенных этим оператором). Вот пример:

print ".", (not 0), "\n";

print ".", (not 1), "\n";

.1.

.. 

(Здесь в качестве логического значения ложь выводится пустая строка.)

Логическое XOR

Логическое AND

Оператор and работает аналогично &&, но имеет более низкий приоритет. Он сперва вычисляет левый операнд, а его правый операнд вычисляется только в том случае, когда соответствует условию истина. Другими словами, оператор and работает по сокращенной схеме, как и оператор &&. (В разделе, посвященном оператору or, показано, как это можно использовать.)

Логическое OR

Оператор or работает аналогично ||, но имеет более низкий приоритет. Низкий приоритет оператора гарантирует, что его можно использовать при вызовах списковых операторов без необходимости заключать списки в круглые скобки.

Оператор or сперва вычисляет левый операнд, а правый операнд вычисляется только в том случае, если левый соответствует условию ложь. Другими словами, or работает по такой же сокращенной схеме, что и оператор ||.

Если хотя бы один из операндов соответствует условию истина, то оператор or возвращает значение первого такого операнда в качестве результата (поскольку в Perl любое ненулевое и непустое значение рассматривается как истина, возможна масса вариантов). Это можно использовать для сокращения числа условных операторов. Например, в следующем фрагменте кода делается попытка открыть файл, а в случае неудачи выводится сообщение об ошибке и работа программы прекращается с помощью функции die:

open FileHandle, $filename or die "Cannot open $filename\n";

(Так как приоритет оператора or меньше, чем у операторов open и die, скобки не нужны.)

Логическое XOR

Оператор xor возвращает логическое «исключающее или» для двух окружающих его операндов. Работа xor аналогична работе оператора ^, но его приоритет существенно ниже.

Глава 5.Условные операторы и циклы

Краткое введение

В этой главе рассказывается, как с помощью условных операторов и циклов управлять порядком выполнения команд Perl. Кроме того, будут рассмотрены и другие команды, управляющие ходом выполнения сценария — например, goto, exit и die.

Условные операторы

Условные операторы, также называемые операторами ветвления, позволяют направлять выполнение сценария в ту или иную сторону, в зависимости от результата логической проверки. Иными словами, с их помощью на этапе выполнения кода можно принимать решения и действовать в соответствии с последними.

Например, следующий фрагмент сценария использует команду if для проверки значения переменной $variable. Если значение равно пяти, выводится текст «Yes, it is five.». В противном случае на экране появляется строка «No, it is not five.»:

$variable = 5;

if ($variable == 5) {

print "Yes, it’s five.\n";

} else {

print "No, it’s not five.\n";

}

Yes, it’s five.

Даже этот простой пример показывает возможности команды if. Она проверяет условие, заданное в круглых скобках и, если результат вычислений соответствует истине (то есть не является нулем, пустой строкой и т. д.), программа будет выполнять первый блок кода. В противном случае осуществляется переход к блоку else (он может и отсутствовать).

Команда if является составной, то есть входящий в нее блок (или блоки) кода выделяются фигурными скобками. Поскольку Perl пропускает «пробельные символы», включая символы перевода строки, предыдущую команду можно записать как

$variable = 5;

if ($variable == 5)

{

print "Yes, it’s five.\n";

}

else

{

print "No, it’s not five.\n";

}

Однако, запрещается использовать синтаксическую конструкцию в стиле языка С, когда фигурные скобки опускаются. Даже, если блок состоит из одной команды — Perl всегда требует наличия фигурных скобок:

$variable = 5;

if ($variable == 5) # Это неправильно!

print "Yes, it’s five.\n";

else

print "No, it’s not five.\n";

Условные операторы, подобные if, позволяют управлять порядком выполнения программы. В этом и есть суть программирования — правильно принять решение.

Операторы цикла

Операторы цикла также являются мощным инструментом программирования, потому что позволяют выполнять итерационные операции над наборами данных. Это именно то, в чем компьютеры превосходят людей — быстрые повторяющиеся вычисления. Оператор цикла продолжает выполнять команды, входящие в его тело, пока не будет выполнено заданное условие.

Цикл while уже не раз встречался в этой книге. В следующем примере он используется для построчного вывода содержимого входного файла:

while (<>) {

print;

}

Более сложные циклы могут использовать индекс цикла. Например, этот цикл for вычисляет значение факториала:

$factorial = 1;

for ($loop_ind = 1; $loop_ind <= 6; $loop_ind++) {

$factorial *= $loop_ind;

}

print "6! = $factorial\n";

6! = 720

а этот цикл foreach позволяет перебрать указанный список значений:

foreach $variable1 ("one", "two", "three")

{

print "/$variable1/";

}

/one//two//three/

С помощью индекса цикла можно ссылаться на данные из некоторого набора, последовательно обрабатывая значение за значением. В качестве примера приведем цикл, перебирающий элементы массива:

@array = ("one", "two", "three");

for ($loop_ind = 0; $loop_ind <= &#array; $loop_ind++)

{

print $array[$loop_ind] . " ";

}

one two three

Операторы цикла способны выполнять и более сложные действия — так, блок continue, помещенный в конце цикла, задает последовательность операций, которая выполняется в любом случае (точнее, почти в любом случае) после завершения основной части цикла:

@array = ("one", "two", "three");

$loop_ind = 0;

while ($loop_ind <= &#array)

{

print $array[$loop_ind] . " ";

}

continue

{

$loop_ind++;

}

one two three

Команда redo, указанная в теле цикла или разделе continue, передает управление в начало тела цикла (минуя заголовок цикла), команда last обеспечивает выход из цикла, минуя раздел continue, команда next передает управление разделу continue с последующим переходом к новой итерации (если только в этом разделе нет других команд передачи управления или итерация не является последней). Пример — «ручное» управление вырожденным циклом for, имитирующее работу цикла `foreach $index (1, 2, 3, 4, 5)’:

for ($index = 1; $index == 1; $index = 1) {

if ($index > 5) {last}; # выход из цикла

print "Hello";

if ($index == 5) {next}; # переход сразу к continue

print "... ";

} continue {

$loop_index++; # изменение индекса

redo; # вернуться в начало цикла, игнорировав

# команды, указанные в заголовке цикла

}

print "!\n";

Hello... Hello... Hello... Hello... Hello.!

(Обратите внимание, как команда redo в блоке continue обходит заголовок цикла при передаче управления в начало тела цикла — в противном случае проверка индекса и присвоение индексу единицы испортили бы «нормальную» работу этого примера.)

На этом закончим с введением. По существу условные операторы позволяют принимать решения по ходу выполнения кода, а операторы цикла предоставляют возможность осуществлять повторяющиеся операции с данными. Оба класса команд являются мощными инструментами. Поэтому посмотрим, как заставить их работать.

Немедленные решения

Условный оператор if

Оператор if является базовым условным оператором в Perl. Он проверяет условие, заданное в круглых скобках, и, если результат вычислений дает ненулевое значение, выполняется блок команд, ассоциированный с данной командой. (Пустые строки, неопределенные переменные, значения undef и т. д. в данном контексте также рассматриваются как логический ноль.) Можно также задать блок команд, выполняемых в случае ложности проверяемого условия. Это делается с помощью блока else. Конструкция elsif (обратите внимание: не else if и не elseif) продолжает проверку дополнительных условий. Вот как записывается эта команда:

if (выражение) {блок}

if (выражение) {блок} else {блок}

if (выражение) {блок} elsif {блок} ... else {блок}

Рассмотрим пример. Оператор проверки на равенство оценивает, равно ли значение указанной переменной пяти, и если это так, сообщает о результате пользователю:

$variable = 5;

if ($variable == 5) {

print "Yes, it’s five.\n";

}

Yes, it’s five.

Можно добавить дополнительный раздел else, который будет информировать пользователя о том, что проверка не прошла:

$variable = 6;

if ($variable == 5) {

print "Yes, it’s five.\n";

} else {

print "No, it’s not five.\n";

}

No, it’s not five.

Наконец, для выполнения произвольного количества проверок можно добавить разделы elsif:

$variable = 2;

if ($variable == 1) {

print "Yes, it’s one.\n";

} elsif ($variable == 2) {

print "Yes, it’s two.\n";

} elsif ($variable == 3) {

print "Yes, it’s three.\n";

} elsif ($variable == 4) {

print "Yes, it’s four.\n";

} elsif ($variable == 5) {

print "Yes, it’s five.\n";

} else {

print "Sorry, can’s match it!\n";

}

Yes, it’s two

Команда unless

Команда unless является как бы изнанкой if: она работает так же, но ассоциированный с условием блок выполняется, если условие оказывается ложью. Вот как выглядит эта команда:

unless (выражение) {блок}

unless (выражение) {блок} else {блок}

unless (выражение) {блок} elsif {блок} ... else {блок}

Вот пример с использованием цикла while:

while (<>) {

chomp;

unless (/^q/i) {

print;

} else {

exit;

}

}

Эта программа печатает текст, который вводится пользователем, если только пользователь не введет строчку, начинающуюся с литер q или Q (сокращение от quit или QUIT). В противном случае работа программы прекращается. Проверка условия выполняется за счет сравнения текста, хранящегося в переменной $_, с шаблоном (шаблоны и условия совпадения текста с шаблоном рассматриваются в главе 6).

Оператор цикла for

Оператор цикла for используется для итерационного выполнения команд, находящихся в теле цикла. Обычно при выполнении итерации используется переменная-индекс цикла. Общий вид оператора цикла for выглядит как:

метка for (выражение1; выражение2; выражение3) {блок}

Первое выражение вычисляется перед началом цикла. Второе выражение вычисляется перед началом каждой итерации и, если оно оказывается ложью, выполнение цикла прерывается. Третье выражение вычисляется в конце каждой итерации. Метка, то есть идентификатор, заканчивающийся двоеточием, используется для передачи управления в начало цикла в случае, когда нормальное выполнение тела цикла прерывается (см. описание команд next, redo и last в последующих разделах).

Подсказка: Если при первом входе в цикл, проверяемое условие оказывается ложным, тело цикла не выполняется вообще.

Имеется множество путей использования оператора цикла for. Классический способ состоит в том, чтобы определить переменную цикла и изменять ее значение с заданным шагом, пока она не выйдет за некоторые границы. В следующем примере мы используем переменную цикла $loop_ind, чтобы пять раз напечатать строку "Hello!\n":

for ($loop_ind = 1; $loop_ind <= 5; $loop_ind++) {

print "Hello!\n"

}

Hello!

Hello!

Hello!

Hello!

Hello!

Не возбраняется использовать несколько переменных цикла:

for ($loop_ind = 0, $double = 0.0, $text =’\n’;

$loop_ind <= 4;

$loop_ind++, $double = 2.0 * loop_ind) {

print "Loop index " . $loop_ind .

" doubled equals " . $double . $text;

}

Loop index 0 doubled equals 0

Loop index 1 doubled equals 2

Loop index 2 doubled equals 4

Loop index 3 doubled equals 6

Loop index 4 doubled equals 8

По завершении цикла можно проверить переменную-индекс, чтобы узнать, сколько было выполнено итераций:

$factorial = 1;

for ($ind_loop = 1; $ind_loop <= 6; $ind_loop++) {

$factorial *= $ind_loop;

}

print $ind_loop-1 . "! = $\factorial\n";

6! = 720;

Предупреждение: Так поступать не рекомендуется, поскольку работоспособность такого кода в последующих версиях Perl не гарантируется (то есть переменная цикла в дальнейшем может не сохранять свое последнее значение при выходе из цикла).

Если вы хотите, чтобы переменные цикла были недоступны вне его, используйте при их описании спецификатор my:

$factorial = 1;

for (my $ind_loop = 1; $ind_loop <= 6; $ind_loop++) {

$factorial *= $ind_loop;

}

(см. также раздел «Управление областью видимости (ключевые слова my и local)» в главе 5).

На самом деле, при работе с for нет необходимости увеличивать индекс цикла и проверять его значение. В следующем примере из входного потока данных STDIN считываются и выводятся строки до тех пор, пока не встретится строка, начинающаяся с букв q или Q (что является сокращением для quit или QUIT). Обратите внимание, что входной текст необходимо присвоить промежуточной переменной $line, поскольку ввод из входного потока направляется по умолчанию в переменную $_ только в случае цикла while:

for ($line = <>; $line =~ /^q/i; ; $line = <> {

print $line;

}

Оператор цикла foreach, рассматриваемый в следующем разделе, иногда используется идентично оператору цикла for (по сути дела в Perl они выполняют одни и те же действия). В следующем примере оператор цикла foreach полноценно работает с переменной-индексом цикла:

foreach ($loop_ind = 1; $loop_ind <= 5; $loop_ind++) {

print "Hello!\n";

}

Hello!

Hello!

Hello!

Hello!

Hello!

Наконец, рассмотрим также пример, когда оператор for работает наподобие foreach:

@array = ("Hello ", "there.\n");

for (@array) {print;}

Hello there.

Оператор цикла foreach

В Perl оператор цикла foreach является синонимом for. Однако, когда требуется, чтобы переменная цикла последовательно перебирала данные из некоторого заданного списка значений, программисты зачастую используют в явном виде оператор foreach (чтобы подчеркнуть, что смысл цикла выражается фразой «for each element in ...»). Этот оператор цикла записывается как:

метка foreach переменная (список) {блок}

Во время итерации из списка извлекается очередное значение, присваивается переменной и выполняется тело цикла. Цикл завершается, когда список исчерпан. Как и раньше, метка используется для передачи управления в начало цикла в случае, когда нормальное выполнение очередной итерации прерывается (см. описание команд next, redo и last в последующих разделах).

Если в заголовке цикла не задано имя переменной, используется имя $_ (это удобно при работе с функциями, использующими $_ как аргумент по умолчанию — например, print):

@array = ("Hello ", "there.\n");

foreach (@array) {print;}

Hello there.

Если перед переменной цикла указано ключевое слово my, то она будет определена только внутри тела цикла. Если my отсутствует, переменная все равно будет иметь локальную область видимости — а именно, при выходе из цикла ее последнее (внутри тела цикла) значение теряется, и переменная восстанавливает значение, которое она имела перед входом в цикл. Соответственно, использование переменной цикла (с ключевым словом my или без него) для подпрограмм и форматов, определенных внутри цикла, может привести к ошибкам.

Оператор foreach можно использовать и для итерации по содержимому хеша, используя результат работы функции keys или values:

$hash{fruit} = orange;

$hash{sandwich} = clubburger;

$hash{drink} = lemonade;

foreach $key (keys %hash) {

print $hash{$key} . "\n";

}

lemonade

clubburger

orange

У цикла foreach есть одна особенность: вместо того, чтобы присваивать переменной значение из списка, переменная становится синонимом этого значения. В частности, если очередным элементом списка является имя переменной, то изменение значения переменной цикла изменит и эту переменную. Если в качестве списка выступает переменная-массив, то изменения сказываются на ее содержимом:

@array = (1, 2, 3);

foreach $element (@array) {

$element += 1;

}

print join(", ", @array);

2, 3, 4

Не следует изменять структуру списка, используемого как аргумент команды foreach, в процессе работы цикла (например, с помощью функции splice, примененной к массиву, указанному вместо списка) — в противном случае цикл, скоре всего, будет работать неправильно. Аналогично, не стоит использовать в качестве переменной цикла специальные переменные Perl или имена, связанные с другими объектами (с помощью функции tie или метода TIESCALAR, например).

Функция each позволяет организовывать работу с хешами в стиле, напоминающем выполнение цикла foreach. Она последовательно возвращает пары ключ/значение, хранящиеся в хеше, как показано в следующем примере:

$hash{fruit} = orange;

$hash{sandwich} = clubburger;

$hash{drink} = lemonade;

while (($key, $value) = each(%hash)) {

print "$key => $value\n";

}

drink => lemonade

fruit => orange

sandwich => clubburger

Оператор цикла while

Оператор while играет важную роль в Perl. Вот как он выглядит:

метка while (выражение) {блок}

метка while (выражение) {блок} continue {блок’}

Тело цикла выполняется, пока выражение в заголовке цикла остается истинным (перед каждым выполнении тела цикла оно вычисляется повторно). Метка используется для передачи управления в начало цикла в случае, когда нормальное выполнение тела цикла прерывается (см. описание команд next, redo и last в последующих разделах).

Следующий пример суммирует доходы пользователя до тех пор, пока сумма не превысит миллион<$FВообще говоря, для работоспособности данного примера необходимо удалять конец строки после ввода значения с терминала ($_ = <>; chomp $_;). Кроме того, постоянное использование автором chop вместо chomp — тоже дурной стиль. — Примеч. ред.>:

$savings = 0;

while ($savings < 1_000_000) {

print "Enter the amount you earned today: ";

$savings +- <>;

}

print "Congratulations, millionaire.\n";

А в этом примере (он используется также в предыдущем разделе) цикл while применяется для перебора всех значений хеша с помощью функции each, возвращающей при каждом обращении очередную пару ключ/значение:

$hash{fruit} = orange;

$hash{sandwich} = clubburger;

$hash{drink} = lemonade;

while (($key, $value) = each(%hash)) {

print "$key => $value\n";

}

drink => lemonade

fruit => orange

sandwich => clubburger

Специальная форма цикла — цикл while (<>) — имеет полезное свойство: внутренняя переменная Perl $_ автоматически заполняется данными, построчно вводимыми через стандартный поток ввода. Это означает, что можно с пользой применять многочисленные функции Perl, использующие переменную $_ как аргумент по умолчанию:

while (<>) {

print;

}

Блок continue, если он задан, выполняется всякий раз, когда тело цикла выполнено полностью или частично (см. далее раздел «Команда next: как перейти к следующей итерации цикла») перед очередной проверкой условия цикла. Например, с помощью блока continue, можно заставить цикл while вести себя наподобие for:

$loop_index = 1;

while ($loop_index <= 5) {

print "Hello!\n";

} continue {

$loop_index++;

}

Hello!

Hello!

Hello!

Hello!

Hello!

Оператор while проверяет условие перед выполнением тела цикла, так что оно может вообще ни разу не выполниться. Это удобно, если тело цикла организовано таким образом, что его выполнение при нарушенном условии цикла может вызывать проблемы. Например, в следующем примере программа не будет печатать строки, вводимые из файла, если дескриптор файла FileHandle не связан ни с одним файлом:

while (<FileHandle>) {

print;

}

Оператор цикла until

Оператор until выполняет те же функции, что и while, за тем исключением, что для выполнения тела цикла требуется, чтобы проверяемое условие было ложью. Этот цикл записывается как:

метка until (выражение) {блок}

метка until (выражение) {блок} continue {блок’}

По аналогии с примером из предыдущего раздела, напечатаем пять раз подряд с помощью оператора цикла until текст "Hello!\n":

$loop_index = 1;

until ($loop_index > 5) {

print "Hello!\n";

} continue {

$loop_index++;

}

Hello!

Hello!

Hello!

Hello!

Hello!

Модификаторы if, unless, until,while и for

Кроме формальных конструкций условных операторов и операторов цикла Perl позволяет добавлять модификаторы с аналогичными функциями в конец любой стандартной команды:

if выражение

unless выражение

while выражение

until выражение

for (список)

foreach (список)

Работа этих модификаторов в значительной степени повторяет работу стандартных операторов цикла и условного оператора, но они зачастую облегчают чтение кода. Например, вот как печатается сообщение "Too big!\n" с помощью модификатора if, если пользователь вводит число больше ста:

while (<>) {

print "Too big!\n" if $_ > 100;

}

В следующем примере модификатор unless используется, чтобы вывести сообщение об ошибке и выйти из программы, если открыть файл невозможно:

die "Cannot open the file.\n" unless open($filename);

Точно так же, например, для печати входного потока вместо цикла while с телом, состоящим из одной команды print, можно использовать команду print с модификатором while:

print while (<>);

Модификаторы for и foreach (которые отличаются друг от друга только записью, но не действием) требуют дополнительного разъяснения. В отличие от операторов for и foreach, модификаторы for и foreach не позволяют задавать переменную-индекс — для этой цели всегда используется специальная переменная $_, подразумеваемая по умолчанию, а в качестве условия, определяющего итерации, служит список значений, а не тройка команд. Во всем остальном работа модификаторов не отличается от работы операторов цикла:

print $_ foeach (1, 2, 3, 4, 5, 6, 7, 8);

12345678

Подсказка: Особенности работы модификаторов while и until с командой do рассматривваются в следующем разделе.

Как создать цикл do while

Многие программисты думают, что если в языке программирования есть цикл while, то должен быть и цикл do while. Однако в Perl\ это не так. Точнее, нет отдельного цикла do while, но есть команда do, которая записывается как:

do {блок}

do подпрограмма (параметры) # Не рекомендуется

do выражение

Команда do {блок команд} выполняет указанную последовательность команд и возвращает значение, соответствующее последней выполненной команде. Команда do подпрограмма (параметры) выполняет вызов подпрограммы, и ее рекомендуется заменять на более стандартную команду call (или оформлять вызов подпрограммы в виде блока). Команда do выражение интерпретирует выражение как имя файла (например, do "myscript.pl") и выполняет поток команд, содержащихся в этом файле.

Если использовать команду do с модификатором while, то мы получим конструкцию, имитирующую поведение цикла do while:

do {

print;

} while (<>);

Необходимо отметить, что команда do с модификатором while будет выполнена по крайней мере один раз, то есть даже в том случае, если условие оказывается ложью с самого начала (это — один из примеров чувствительности конструкций Perl к контексту, в котором они используются). Например, в отличие от команды «print "ABC" while (0);», которая не будет выполнена ни разу, команда «do {print "ABC"} while (0);» напечатает текст.

Подсказка: Точно таким же образом совместно с оператором do работает и модификатор until. А именно, команда «do {print "ABC"} until (1);» напечатает текст "ABC", хотя и один раз.

Глава 6.Регулярные выражения

Коротко

Работа с текстом — это то, в чем Perl превосходит другие программы. Регулярные выражения обеспечивают значительную часть возможностей Perl по обработке текстов. Они позволяют сопоставлять текст с указанным шаблоном (а именно, сравнивать две строки с помощью универсальных символов, интерпретируемых специальным образом) и выполнять замену текста. Таким образом Perl предоставляет мощный инструмент для манипулирования текстом под управлением программы пользователя.

С другой стороны, не подлежит сомнению, что регулярные выражения Perl — это одна из тех областей, которые требуют серьезных усилий со стороны программиста. Зачастую требуется время для того, чтобы разобраться в том, что делают даже относительно прямолинейные и однозначные конструкции. Например, следующее регулярное выражение осуществляет поиск в тексте маркеров HTML <A> и <IMG> , а также текста, заключенного внутри разметки вплоть до концевых маркеров </A> и </IMG>):

$text = "<A>Here is an anchor.</A>";

if ($text =~ /<([IMG|A])>[\w\s\.]+<\/\1>/i) {

print "Found an image or anchor tag.";

}

Found an image or anchor tag

Это наиболее сокровенная область языка Perl. А потому я постараюсь внести в этот вопрос максимальную ясность<$FТем не менее необходимо отметить, что материал, изложенный здесь, является лишь введением в эту важную область — регулярным выражениям и особенностям работы с ними посвящены целые книги, подробно рассматривающие специальные приемы, трюки и готовые решения. — Примеч. ред.>.

Использование регулярных выражений

В Perl имеются три основных оператора, работающих со строками:

m/.../ — проверка совпадений (matching),

s/.../.../ — подстановка текста (substitution),

tr/.../.../ — замена текста (translation).

Оператор m/.../ анализирует входной текст и ищет в нем подстроку, совпадающую с указанным шаблоном (он задан регулярным выражением). Оператор s/.../.../ выполняет подстановку одних текстовых фрагментов вместо других, используя для этой цели регулярные выражения. Оператор tr/.../.../ также изменяет входной текст, но при этом он не использует регулярные выражения, осуществляя замену посимвольно.

Оператор проверки совпадений m/.../

Оператор m/.../ пытается сопоставить шаблон, указанный в качестве аргумента, и заданный текст (по умолчанию текст берется из переменной Perl $_). В приведенном ниже примере мы ищем во вводимом пользователем тексте строку exit (модификатор i после второй наклонной черты делает проверку нечувствительной к регистру):

while (<>) {

if (m/exit/i) {exit;}

}

Вместо того, чтобы использовать переменную $_, можно задать источник проверяемого текста с помощью оператора =~. В нашем примере для этой цели используется переменная $line (данный код не меняет содержимого переменной $line, хотя оператор =~ и напоминает символ присвоения):

while ($line = <>) {

if ($line =~ m/exit/i) {exit;}

}

Смысл сравнения можно изменить на противоположный, если вместо оператора =~ использовать оператор !~:

while ($line = <>) {

if ($line !~ /exit/i) {} else {exit;}

}

Поскольку в Perl оператор m/.../ используется очень часто, можно использовать его сокращенную форму, опустив начальную букву m. Если же начальная буква m присутствует, то вместо символов наклонной черты (слеша) в качестве ограничителей можно использовать, за редким исключением, любой другой символ (см. далее описание оператора подстановки s/.../.../):

while ($line = <>) {

if ($line =~ /exit/i) {exit;}

if ($line =~ m|quit|i) {exit;}

if ($line =~ m%stop%i) {exit;}

}

Подсказка: Если шаблон содержит символы косой черты, что зачастую встречается, например, при анализе имен файлов с указанием пути и/или меток HTML, то стоит отказаться от использования этих символов в качестве ограничителя. Это позволит не ставить обратную косую черту перед каждым символом косой черты внутри шаблона и сохранит его прозрачность.

Оператор подстановки s/.../.../

Оператор s/.../.../ выполняет замену одних фрагментов текста на другие. Например, в следующем случае мы заменяем подстроку young на подстроку old:

$text = "Pretty young.";

$text =~ s/young/old/;

print $text;

Pretty old.

По умолчанию оператор замены работает с переменной Perl $_. Как и в случае оператора m/.../, косую черту использовать не обязательно — годится любой символ, который не вступает в противоречие с заданным выражением. Например, вместо косой черты можно использовать |:

$text = "Pretty young.";

$text =~ s|young|old|;

print $text;

Pretty old.

либо просто поставить в качестве ограничителя обычные скобки

$text = "Pretty young.";

$text =~ s(young)(old);

print $text;

Pretty old.

Более подробно о форме записей команд m/.../ и s/.../.../ рассказывается далее в разделе «Особенности работы команд m/.../ и s/.../.../».

Подсказка: Старайтесь не применять в качестве ограничителей вопросительный знак (?) и апостроф (’)— шаблоны, ограниченные этими символами, обрабатываются иначе, чем обычные (см. раздел «Особенности работы команд m/.../ и s/.../.../» далее в этой главе).

Обратите внимание, что операторы s/.../.../ и m/.../ ведут поиск с первого символа текстовой строки до первого совпадения. Если оно найдено, без специального указания поиск не продолжается:

$text = "Pretty young, but not very young.";

$text =~ s/young/old/;

print $text;

Pretty old, but not very young.

Оператор замены tr/.../.../

Кроме операторов m/.../ и s/.../.../, для работы со строками в Perl имеется оператор tr/.../.../. Он также выполняет замену одних фрагментов текста на другие, однако, в отличие от s/.../.../, не пытается обрабатывать регулярные выражения, подставляя текст один к одному. В следующем примере мы заменяем с его помощью букву «o» на букву «i»:

$text = "His name is Tom.";

$text =~ tr/o/i/;

print $text;

His name is Tim.

Подсказка: В Perl операторы tr/.../.../ и y/.../.../ выполняют одинаковые действия. Точнее, tr и y — это два имени одного и того же оператора.

Итак, мы перечислили операторы, с которыми будем работать в этой главе. Наше введение, однако, лишь едва затронуло рассматриваемую тему. Теперь наступает время изучить создание регулярных выражений всерьез, что позволит полноценно работать с поиском и заменой строк.

Непосредственные решения

Создание регулярных выражений

Регулярные выражения —основа работы с операторами m/.../ и s/.../.../, так как они передаются последним в качестве аргументов. Разберемся, как устроено регулярное выражение \b([A-Za-z]+)\b, осуществляющее поиск отдельных слов в строке:

$text = "Perl is the subject.";

$text =~ /\b([A-Za-z]+)\b/;

print $1;

Perl

Выражение \b([A-Za-z]+)\b включает в себя группирующие метасимволы ( и ), метасимвол границы слова \b, класс всех латинских букв [A-Za-z] (он объединяет заглавные и строчные буквы) и квантификатор +, который указывает на то, что требуется найти один или несколько символов рассматриваемого класса.

Поскольку регулярные выражения, как это было в предыдущем примере, могут быть очень сложными, в этой главе они разбираются по частям. В общем случае регулярное выражение состоит из следующих компонентов:

одиночные символы (characters)

классы символов (character classes)

альтернативные шаблоны (alternative match patterns)

квантификаторы (quantifiers)

мнимые символы (assertions)

ссылки на найденный текст (backreferences)

дополнительные конструкции (regular expression extensions).

Каждый из этих элементов достоин особого изучения. Их обсуждению посвящены несколько следующих разделов.

Одиночные символы в регулярных выражениях

В регулярном выражении любой символ соответствует самому себе, если только он не является метасимволом со специальным значением (такими метасимволами являются \, |, (, ), [, {, *, +, ^, $, ? и .). В следующем примере проверяется, не ввел ли пользователь команду «quit» (и если это так, то прекращаем работу программы):

while {<>} {

if (m/quit/) {exit;}

}

Правильнее проверить, что введенное пользователем слово «quit» не имеет соседних слов, изменяющих смысл предложения. (Например, программа выполнит заведомо неверное действие, если вместо «quit» пользователь введет команду «Don’t quit!».) Это можно сделать с помощью метасимволов ^ и $. Заодно, чтобы сравнение было нечувствительно к разнице между прописными и заглавными буквами, используем модификатор i:

while {<>} {

if (m/^quit$/i) {exit;}

}

(О работе метасимволов ^ и $ рассказывается в разделе «Мнимые символы в регулярных выражениях». О модификаторе i можно подробнее узнать в разделе «Модификаторы команд m/.../ и s/.../.../».)

Кроме обычных символов, Perl определяет специальные символы. Они вводятся с помощью обратной косой черты (escape-последовательности) и также могут встречаться в регулярном выражении:

\077 — восьмиричный символ \a — символ BEL (звонок) \c[ — управляющие символы (комбинация Ctrl + символ, в данном случае — это управляющий символ ESC) \d — соответствует цифре \D — соответствует любому символу, кроме цифры \e — символ escape (ESC) \E — конец действия команд \L, \U и \Q \f — символ прогона страницы (FF) \l — следующая литера становится строчной (lowercase) \L — все последующие литеры становятся строчными вплоть до команды \E \n — символ новой строки (LF, NL) \Q — вплоть до команды \E все последующие метасимволы становятся обычными символами \r — символ перевода каретки (CR) \s — соответствует любому из «пробельных символов» (пробел, вертикальная или горизонтальная табуляция, символ новой строки, и т. д.) \S — любой символ, кроме «пробельного» \t — символ горизонтальной табуляции (HT, TAB) \u — следующая литера становится заглавной (uppercase) \U — все последующие литеры становятся заглавными вплоть до команды \E \v — символ вертикальной табуляции (VT) \w — алфавитно-цифровой символ (любая буква, цифра или символ подчеркивания) \W — любой символ, кроме букв, цифр и символа подчеркивания \x1B — шестнадцатиричный символ

Вы также можете «защитить» любой метасимвол, то есть заставить Perl рассматривать его как обыкновенный символ, а не как команду, поставив перед метасимволом обратную косую черту \.

Обратите внимание на символы типа \w, \d и \s, которые соответствуют не одному, а любому символу из некоторой группы. Также заметьте, что один такой символ, указанный в шаблоне, соответствует ровно одному символу проверяемой строки. Поэтому для задания шаблона, соответствующего, например, слову из букв, цифр и символов подчеркивания, надо использовать конструкцию \w+, как это сделано в следующем примере:

$text = "Here is some text.";

$text =~ s/\w+/There/;

print $text;

There is some text.

(Знак + означает «один или более символов, соответствующих шаблону». Более подробно о нем рассказано в разделе «Квантификаторы в регулярных выражениях».)

Совпадение с любым символом

В Perl имеется еще один мощный символ — а именно, точка (.). В шаблоне он соответствует любому знаку, кроме символа новой строки. Например, следующая команда заменяет в строке все символы на звездочки (использован модификатор g, обеспечивающий глобальную замену):

$text = "Now is the time.";

$text =~ s/./*/g;

print $text;

****************

А что делать, если требуется проверить совпадение именно с точкой? Символы вроде точки (конкретно, \|()[{^$*+?.), играющие в регулярном выражении особую роль) называются, как уже было сказано выше, метасимволами, и если вы хотите, чтобы они внутри шаблона интерпретировались как обычные символы, метасимволу должна предшествовать обратная косая черта. Точно так же обратная косая черта предшествует символу, используемому в качестве ограничителя для команды m/.../, s/.../.../ или tr/.../.../, если он встречается внутри шаблона и не должен рассматриваться как ограничитель. Рассмотрим пример:

$line = ".Hello!";

if ($line =~ m/^\./) {

print "Shouldn’t start a sentence with a period!\n";

}

Shouldn’t start a sentence with a period!

Классы символов в регулярных выражениях

Символы могут быть сгруппированы в классы. Указанный в шаблоне класс символов, сопоставляется с любым из символов, входящим в этот класс. Класс — это список символов, заключенный в квадратные скобки [ и ]. Можно указывать как отдельные символы, так и их диапазон (диапазон задается двумя крайними символами, соединенных тире).

Например, следующий код производит поиск гласных:

$text = "Here is the text.";

if ($text =~ /[aeiou]/) {print "Vowels: we got ‘em.\n";}

Vowels: we got ‘em.

Другой пример: с помощью шаблона [A-Za-z]+ (метасимвол + означает утверждение: «один или более таких символов» — см. далее раздел «Квантификаторы в регулярных выражениях») ищется и заменяется первое слово:

$text = "What is the subject.";

$text =~ s/[A-Za-z]+/Perl/;

print $text;

Perl is the subject.

Подсказка: Если требуется задать минус как символ, входящий в класс символов, перед ним надо поставить обратную косую черту (\-).

Если сразу после открывающей квадратной скобки стоит символ ^, то смысл меняется на противоположный. А именно, этот класс сопоставляется любому символу, кроме перечисленных в квадратных скобках. В следующем примере производится замена фрагмента текста, составленного не из букв и не из пробелов:

$text = "Perl is the subject on page 493 of the book.";

$text =~ s/[^A-Za-z\s]+/500/;

print $text;

Perl is the subject on page 500 of the book.

Альтернативные шаблоны в регулярных выражениях

Вы можете задать несколько альтернативных шаблонов, используя символ | как разделитель. Альтернативные шаблоны позволяют превратить процедуру поиска из однонаправленного процесса в разветвленный: если не подходит один шаблон, Perl подставляет другой и повторяет сравнение, и так до тех пор пока не иссякнут все возможные альтернативные комбинации. Например, следующий фрагмент проверяет, не ввел ли пользователь «exit», «quit» или «stop»:

while (<>) {

if (m/exit|quit|stop/) {exit;}

}

Чтобы было ясно, где начинается и где заканчивается набор альтернативных шаблонов, их заключают в круглые скобки — иначе символы, расположенные справа и слева от группы шаблонов, могут смешаться с альтернативными шаблонами.

В следующем примере метасимволы ^ и $ обозначают начало и конец строки (см. раздел «Мнимые символы в регулярных выражениях»), и отделяются от набора альтернативных шаблонов с помощью скобок:

while (<>) {

if (m/^(exit|quit|stop)$/) {exit;}

}

Альтернативные варианты перебираются слева направо. Как только найдена первая альтернатива, для которой выполняется совпадение с шаблоном, перебор прекращается.

Подсказка 1: Участки шаблона, заключенные в круглые скобки, выполняют специальную роль при выполнении операций поиска и замены. Об этой особенности круглых скобок рассказывается в разделах «Ссылки на найденный текст» и «Особенности работы команд m/.../ и s/.../.../».

Подсказка 2: Если символ | находится в квадратных скобках, он интерпретируется как обычный символ. Поэтому если вы используете конструкцию шаблона вида [Tim|Tom|Tam], то она будет эквивалентна классу символов [Tioam|]. Точно так же большинство других метасимволов и команд, специфичных для регулярных выражений — в частности, квантификаторы и мнимые символы, описанные в двух последующих разделах, — внутри квадратных скобок превращаются в обычные символы или escape-последовательности текстовых строк.

Квантификаторы в регулярных выражениях

Квантификаторы указывают на то, что тот или иной шаблон в строке может повторяться определенное количество раз. Например, можно использовать квантификатор + для поиска мест неоднократного повторения подряд латинской буквы e и их замены на одиночную букву e:

$text = "Hello from Peeeeeeeeeeeeeeerl.";

$text =~ s/e+/e/;

print $text;

Hello from Perl.

Квантификатор + соответствует фразе «один или несколько». Перечислим все доступные в Perl квантификаторы:

* — ноль или несколько совпадений, + — одно или несколько совпадений, ? — ноль совпадений или одно совпадение, {n} — ровно n совпадений, {n,} — по крайней мере n совпадений, {n,m} — от n до m совпадений.

Например, вот как проверить, что пользователь ввел не менее двадцати символов:

while (<>) {

if (!m/.{20,}/) {print "Please type longer lines!\n";}

}

Подсказка: Квантификатор действует только на предшествующий ему элемент шаблона. Например, конструкция \d[a-z]+ будет соответствовать последовательности из одной или нескольких строчных латинских букв, начинающейся с цифры, а не последовательности, составленной из чередующихся цифр и букв. Чтобы выделить группу элементов, на которую действует квантификатор, нужно использовать круглые скобки: (\d[a-z])+.

«Жадность» квантификаторов

Учтите, что квантификаторы количества по умолчанию являются «жадными», то есть возвращают самый длинный фрагмент текста, соответствующий указанному шаблону, начиная с текущей позиции строки. Например, вы хотите заменить фразу «That is some text, isn’t it?» на «That’s some text, isn’t it?», подставив «That’s» вместо «That is». Посмотрим, что получится, если использовать команду:

$text = "That is some text, isn’t it?";

$text =~ s/.*is/That’s/;

print $text;

В силу «жадности» квантификатора * конструкция .*is будет сопоставлена максимально возможному фрагменту текста. То есть, Perl соотнесет с ней все символы, предшествующие последнему «is» (включая и сам «is»). В результате выполнения команды получится:

That’sn’t it?";

Проблеме, как заставить квантификаторы количества быть менее жадными, посвящен отдельный раздел этой главы (см. далее раздел «Как ограничить «жадность» квантификаторов»).

Регулярные выражения, использующие квантификаторы, могут порождать процесс, который называется перебор с возвратом (backtracking). Чтобы произошло совпадение текста с шаблоном, надо построить соответствие между текстом и всем регулярным выражением, а не его частью. Начало шаблона может содержать квантификатор, который поначалу срабатывает, но впоследствии приводит к тому, что для части шаблона не хватает текста или возникает несоответствие между текстом и шаблоном. В таких случаях Perl возвращается назад и начинает построение соответствия между текстом и шаблоном с самого начала, ограничивая «жадность» квантификатора (именно поэтому процесс и называется «перебор с возвратом»).

Мнимые символы в регулярных выражениях

В Perl имеются символы (метасимволы), которые соответствуют не какой-либо литере или литерам, а означают выполнение определенного условия (поэтому в английском языке их называют assertions, или утверждениями). Их можно рассматривать как мнимые символы нулевого размера, расположенные на границе между реальными символами в точке, соответствующей определенному условию:

^ — начало строки текста,

$ — конец строки или позиция перед символом начала новой строки, расположенного в конце,

\b — граница слова, \B — отсутствие границы слова, \A — «истинное» начало строки, \Z — «истинный» конец строки или позиция перед символом начала новой строки, расположенного в «истинном» конце строки,

\z — «истинный» конец строки,

\G — граница, на которой остановился предыдущий глобальный поиск, выполняемый командой m/.../g,

(?= шаблон) — после этой точки есть фрагмент текста, который соответствует указанному регулярному выражению,

(?! шаблон) — после этой точки нет текста, который бы соответствовал указанному регулярному выражению,

(?<= шаблон) — перед этой точкой есть фрагмент текста, соответствующий указанному регулярному выражению,

(?<! шаблон) — перед этой точкой нет фрагмента текста, соответствующего указанному регулярному выражению.

Подсказка: Утверждения вида (?= ...), (?! ...), (?<= ...) и (?<! ...) рассматриваются более детально далее в этой главе в разделах «Дополнительные конструкции в регулярных выражениях» и «Утверждения, проверяющих текст перед шаблоном и после него».

Например, вот как выполнить поиск и замену слова, используя метасимволы границы слов:

$text = "Here is some text.";

$text =~ s/\b([A-Za-z]+)\b/There/;

print $text;

There is some text.

(Perl считает границей слова точку, расположенную между \w и \W, независимо от того, в каком порядке следуют эти символы.)

В следующем примере выводится сообщение о том, что пользователь ввел слово «yes», при условии что оно единственное, что ввел пользователь. Для этого шаблон включает мнимые символы начала и конца строки:

while (<>) {

if (m/^yes$/) {

print "Thank you for being agreeable.\n";

}

}

Приведенный выше пример требует комментария. Прежде всего бросается в глаза наличие двух групп метасимволов для начала и конца строки. В большинстве случаев они означают одно и то же, так как обычно символы новой строки (то есть \n), встречающиеся внутри текстового выражения, не рассматриваются как вложенные строки. Однако, если для команды m/.../ или s/.../.../ указан модификатор m (см. далее раздел «Модификаторы команд m/.../ и s/.../.../»), то текстовое выражение будет рассматриваться как многострочный текст, в котором границами строк выступают символы новой строки \n.

В случае многострочного текста метасимвол ^ сопоставляется с позицией после любого символа новой строки, а не только с началом текстового выражения. Точно также метасимвол $ — это позиция перед любым символом новой строки, расположенным внутри текстового выражения, а не обязательно конец текстового выражения или же позиция перед концевым символом \n. Однако, метасимвол \A — начало текстового выражения, а метасимвол \Z — конец текстового выражения или позиция перед концевым символом \n, даже, если в текстовом выражении имеются вложенные символы \n и при выполнении операции поиска или замены указан модификатор m.

Подсказка: Метасимвол «точка» (.) соответствует любому символу, кроме символа новой строки \n. Независимо от того, задан ли модификатор m, она не будет сопоставляться ни с внутренними, ни с концевыми символами \n. Единственный способ заставить точку рассматривать \n как обычный символ — использовать модификатор s (см. Раздел «Модификаторы команд m/.../ и s/.../.../» далее в этой главе).

Отсюда понятна разница между метасимволами \Z и \z. Если в качестве текстового выражения используется результат чтения входного потока данных, то с большой вероятностью данное выражение заканчивается символом \n, за исключением того варианта, когда программа предусмотрительно «отщипнула» его с помощью функции chop или chomp. Метасимвол \Z игнорирует концевой символ \n, если он случайно остался на месте, рассматривая обе ситуации как «конец строки». В отличие от него, метасимвол \z оказывается более пунктуальным и рассматривает концевой символ \n как неотъемлемую часть проверяемого текстового выражения, если только пользователь не позаботился об удалении этого символа.

Отдельно следует остановиться на метасимволе \G. Он может указываться в регулярном выражении только в том случае, если выполняется глобальный поиск (то есть если команда m/.../ имеет модификатор g — см. раздел «Модификаторы команд m/.../ и s/.../.../»). Метасимвол \G, указанный в шаблоне, соответствует точке, на которой остановилась предыдущая операция поиска. Более подробно о работе алгоритма поиска при наличии модификатора g и метасимвола \G рассказывается далее в разделе «Особенности работы команд m/.../ и s/.../.../».

Предупреждение: В текущей версии Perl метасимвол \G эквивалентен метасимволу \A, если при выполнении операции поиска не указан модификатор g. Однако это — случайное и недокументированное свойство, которое легко может измениться в будущем.

Ссылки на найденный текст

Иногда нужно сослаться на подстроку текста, для которой получено совпадение с некоторой частью шаблона. Например, при обработке файла HTML может потребоваться выделять фрагменты текста, ограниченные открывающими и закрывающими метками HTML (например, <A> и </A>). В начале этой главы уже приводился пример, в котором выделялся текст, ограниченный метками HTML <A> и <IMG>. Следующий пример позволяет выделять текст, расположенный между любыми правильно закрытыми метками:

$text = "<A>Here is an anchor.</A>";

if ($text =~ m%<([A-Za-z]+)>[\w\s\.]+</\1>%i) {

print "HTML tag with some text inside it is found.";

}

HTML tag with some text inside it is found.

(Обратите внимание, что вместо косой черты в качестве ограничителя шаблона использован другой символ. Это позволяет использовать символ косой черты внутри шаблона без предшествующей ему обратной косой черты.)

Каждому фрагменту шаблона, заключенному в круглые скобки, соответствует определенная внутренняя переменная. Переменные пронумерованы, так что на них можно ссылаться внутри шаблона, поставив перед номером обратную косую черту (\1, \2, \3, ...). На значения переменных можно ссылаться внутри шаблона, как на обычный текст, поэтому </\1> соответствует </A>, если открывающей меткой служит <A>, и </IMG>, если открывающей меткой служит </IMG>.

Эти же самые внутренние переменные можно использовать и вне шаблона, ссылаясь на них как на скаляры с именами $1, $2, $3, ... $n:

$text = "I have 4 apples.";

if ($text =~ /(\d+)/) {

print "Here is the number of apples: $1.\n";

}

Here is the number of apples: 4.

Каждой паре скобок внутри шаблона после завершения операции поиска будет соответствовать скалярная переменная с соответствующим номером. Это можно использовать при выделении нужных для последующей работы фрагментов анализируемой строки. В следующем примере мы изменяем порядок трех слов в текстовой строке с помощью команды s/.../.../:

$text = "I see you.";

$text =~ s/^(\w+) *(\w+) *(\w+)/$3 $2 $1/;

print $text;

you see I.

Переменные, соответствующие фрагментам шаблона, нумеруются слева направо с учетом вложенности скобок. Например, после следующей операции поиска будут проинициализированы шесть переменных, соответствующих шести парам скобок:

$text = "ABCDEFGH";

$text =~ m/(\w(\w)(\w))((\w)(\w))/;

print "$1/$2/$3/$4/$5/$6/";

ABC/B/C/DE/D/E

Подсказка 1: Кроме переменных, ссылающихся на найденный текст, можно использовать специальные переменные Perl. Так, $& содержит найденное совпадение (то есть фрагмент текста, для которого найдено соответствие между шаблоном и текстом при последней операции поиска или замены), $` содержит текст перед найденным совпадением, $’ — текст после найденного совпадения, $+ — совпадение для обработанного последним фрагмента шаблона, заключенного в круглые скобки (если у шаблона нет фрагментов, заключенных в круглые скобки, она получает неопределенное значение).

Подсказка 2: Пары скобок, используемые в синтаксических конструкциях вида (?= ...), (?! ...), (?<= ...) и т. д. (см. раздел «Дополнительные конструкции в регулярных выражениях»), не порождают нумерованных переменных.

Дополнительные конструкции в регулярных выражениях

В регулярных выражениях Perl версии 5 имеются дополнительные синтаксические конструкции, использующие скобки в комбинации с вопросительным знаком. Имеют смысл следующие конструкции:

(?#текст) — Комментарий. Текст комментария игнорируется.

(?:шаблон) или (?модификаторы:шаблон) — Группирует элементы шаблона. В отличие от обычных круглых скобок, не создает нумерованной переменной. Модификаторы, которые можно указывать в этой конструкции, описаны далее в разделе «Модификаторы команд m/.../ и s/.../.../». Например, модификатор i не будет делать различия между строчными и заглавными буквами, однако область действия этого модификатора будет ограничена только указанным шаблоном (см. также далее конструкцию (?имя)).

(?=шаблон) — «Заглядывание вперед». Требует, чтобы после текущей точки находился текст, соответствующий данному шаблону. Такая конструкция обрабатывается как условие или мнимый символ, поскольку не включается в результат поиска. Например, поиск с помощью команды /w+(?=\s+)/ найдет слово, за которым следуют один или несколько «пробельных символов», однако сами они в результат не войдут.

(?!шаблон) — Случай, противоположный предыдущему. После текущей точки не должно быть текста, соотносимого с заданным шаблоном. Так, если шаблон w+(?=\s) — это слово, за которым следует «пробельный символ», то шаблон w+(?!\s) — это слово, за которым нет «пробельного символа».

(?<=шаблон) — «Заглядывание назад». Требует, чтобы перед текущей точкой находился соответствующий текст. Так, шаблон (?<=\s)w+ интерпретируется как слово, перед которым имеется «пробельный символ» (в отличие от «заглядывания вперед», «заглядывание назад» может работать только с фиксированным числом проверяемых символов).

(?<!шаблон) — Отрицание предыдущего условия. Перед текущей точкой не должно быть текста, соотносимого с заданным шаблоном. Соответственно, от команды /(?<!\s)w+/ требуется найти слово, перед которым нет «пробельного символа».

(?{код}) — Условие (мнимый символ), которое всегда выполняется. Сводится к выполнению команд Perl в фигурных скобках. Вы можете использовать эту конструкцию, только если в начале сценария указана команда use re 'eval'. При последовательном соотнесении текста и шаблона, когда Perl доходит до такой конструкции, выполняется указанный код. Если полного соответствия для оставшихся элементов найти не удалось, то при возврате левее данной точки шаблона вычисления, проделанные с локальными переменными, откатываются назад. (Условие является экспериментальным. В документации, прилагаемой к Perl, можно найти довольно детальное рассмотрение (с примерами) работы этого условия и возможных трудностей в случае его применения.)

(?>шаблон) — «Независимый» или «автономный» шаблон. Используется для оптимизации процесса поиска, поскольку запрещает «поиск с возвратом». Такая конструкция соответствует подстроке, на которую налагается заданный шаблон, если его закрепить в текущей точке без учета последующих элементов шаблона. Например, шаблон (?>a*)ab, в отличие от a*ab, не может соответствовать никакой строке. Если поставить в любом месте шаблон a*, он «съест» все буквы a, не оставив ни одной шаблону ab. (Для шаблона a*ab «аппетит» квантификатор * будет ограничен за счет работы поиска с возвратами: после того, как на первом этапе не удастся найти соответствие между шаблоном и текстом, Perl сделает шаг назад и уменьшит количество букв a, захватываемых конструкцией a*.)

(?(условие)шаблон-да|шаблон-нет) или (?(условие)шаблон-да) — Условный оператор, который подставляет тот или иной шаблон в зависимости от выполнения заданного условия. Более подробно описан в документации Perl.

(?модификаторы) — Задает модификаторы (они описаны далее в разделе «Модификаторы команд m/.../ и s/.../.../»), которые локальным образом меняют работу процедуры поиска. В отличие от глобальных модификаторов, имеют силу только для текущего блока, то есть для ближайшей группы круглых скобок, охватывающих конструкцию. Например, шаблон ((?i)text) соответствует слову «text» без учета регистра.

Примеры использования дополнительных конструкций в регулярных выражениях приводятся в разделе «Утверждения, проверяющие текст перед шаблоном и после него» в конце этой главы.

Модификаторы команд m/.../ и s/.../.../

В Perl имеется несколько модификаторов, используемых с командами m/.../ и s/.../.../:

i — Игнорирует различие между заглавными и строчными буквами.

s — Метасимволу «точка» разрешено соответствовать символам \n.

m — Разрешает метасимволам ^ и $ привязываться к промежуточным символам \n, имеющимся в тексте. Не влияет на работу метасимволов \A, \Z и \z.

x — Игнорирует «пробельные символы» в шаблоне (имеются в виду «истинные» пробелы, а не метасимволы \s и пробелы, созданные через escape-последовательности). Разрешает использовать внутри шаблона комментарии.

g — Выполняет глобальный поиск и глобальную замену (подробнее — см. следующий раздел).

с — После того, как в скалярном контексте при поиске с модификатором g не удалось найти очередное совпадение, не позволяет сбрасывать текущую позицию поиска (подробнее — см. следующий раздел). Работает только для команды m/.../ и только вместе с модификатором g.

o — Запрещает повторную компиляцию шаблона при каждом обращении к данному оператору поиска или замены. Пользователь, однако, должен гарантировать, что шаблон не меняется между вызовами данного фрагмента кода.

e — Показывает, что правый аргумент команды s/.../../ — это фрагмент выполняемого кода. В качестве текста для подстановки будет использовано возвращаемое значение — возможно, после процесса интерполяции (подробнее — см. следующий раздел).

ee — Показывает, что правый аргумент команды s/.../../ — это строковое выражение, которое надо вычислить и выполнить как фрагмент кода (через функцию eval). В качестве текста для подстановки используется возвращаемое значение — возможно, после процесса интерполяции (подробнее — см. следующий раздел).

(Некоторые модификаторы — например, i, s, m, x — могут находиться в дополнительных конструкциях, рассмотренных в предыдущем разделе.)

В качестве примера рассмотрим сценарий, в котором пользователь выполняет команду выхода, вводя слово «stop», «STOP» или даже «StOp», то есть без учета регистра:

while (<>) {

if (m/^stop$/i) {exit;}

}

Особенности работы команд m/.../ и s/.../.../

До сих пор мы рассматривали регулярные выражения, используемые в качестве шаблонов для команд m/.../ и s/.../.../, и не особо интересовались, как работают эти команды. Настало время восполнить пробелы.

Команда m/.../ ищет текст по заданному шаблону. Ее работа и возвращаемое значение сильно зависят от того, в скалярном или списковом контексте она используется и имеет ли модификатор g (глобальный поиск). Более подробно работа команды m/.../ рассматривается ниже.

Команда s/.../.../ ищет прототип, соответствующий шаблону, и, если поиск оказывается успешным, заменяет его на новый текст. Без модификатора g замена производится только для первого найденного совпадения, с модификатором g выполняются замены для всех совпадений во входном тексте. Команда возвращает в качестве результата число успешых замен, или пустую строку (условие ложь — false), если ни одной заменысделано не было.

В качестве анализируемого текста используется специальная переменная Perl $_ (режим по умолчанию) или выражение, присоединенное к шаблону с помощью оператора =~ или !~. В случае поиска (команда m/.../) конструкция, расположенная слева от операторов =~ или !~, может и не быть переменной. В случае замены (команда s/.../.../) в левой части должна стоять скалярная переменная, или элемент массива, или элемент хеша, или же команда присвоения одному из указанных объектов (см. раздел «Что такое «левое значение»?» в главе 2).

Вместо косой черты в качестве ограничителя для аргументов команд m/.../ и s/.../.../ можно использовать любой символ, за исключением «пробельного символа», буквы или цифры. Например, в этом качестве можно использовать символ комментария, который будет работать как ограничитель:

$text="ABC-abc";

$text =~ s#B#xxx#ig;

print $text;

AxxxC-axxxc

Подсказка: В качестве ограничителей не стоит использовать вопросительный знак и апостроф (одинарную кавычку) — шаблоны с такими ограничителями обрабатываются специальным образом.

Если команда m/.../ использует символ косой черты в качестве разделителя, то букву m можно опустить:

while (defined($text = <>)) {

if ($text =~ /^exit$/i) {exit;}

}

Если в качестве ограничителя для команды m/.../ используется вопросительный знак, то букву m также можно опустить. Однако шаблоны, ограниченные символом ?, в случае поиска работают особым образом (независимо от наличия или отсутствия начальной m). А именно, они ведут себя как триггеры, которые срабатывают один раз и потом выдают состояние ложь (false), пока их не взведут снова, вызвав функцию reset (она очищает статус блокировки сразу всех конструкций ?...?, локальных для данного пакета) Например, следующий фрагмент сценария проверяет, есть ли в файле пустые строки:

while (<>) {

if (?^$?) {print "There is an empty line here.\n";}

} continue {

reset if eof; #очистить для следующего файла

}

Диагностическое сообщение будет напечатано только один раз, даже если в файле присутствует несколько пустых строк.

Предупреждение: Команда поиска с вопросительным знаком относится к «подозрительным» командам, а потому может не войти в новые версии Perl.

В качестве ограничителей можно также использовать различные (парные) конструкции скобок:

while (<>) {

if (m/^quit$/i) {exit;}

if (m(^stop$)i) {exit;}

if (m[^end$]i) {exit;}

if (m{^bye$}i) {exit;}

if (m<^exit$>i) {exit;}

}

В случае команды s/.../.../ и использования скобок, как ограничителей для первого аргумента, ограничители второго аргумента могут выбираться независимо:

$text = "Perl is wonderful.";

$text =~ s/is/is very/;

$text =~ s[wonderful]{beautiful};

$text =~ s(\.)/!/;

print $text;

Perl is very beautiful!

Предварительная обработка регулярных выражений

Аргументами команд m/.../ и s/.../.../ являются регулярные выражения, которые перед началом работы интерполируются подобно строкам, заключенным в двойные кавычки (см. раздел «Подстановка переменных (интерполяция строк)» в главе 2). В отличие от текстовых строк, для шаблона не выполняется интерполяция имен типа $), $| и одиночного $ — Perl считает, что такие конструкции соответствуют метасимволу конца строки, а не специальной переменной. Если же в результате интерполяции шаблон поиска оказался пустой строкой, Perl использует последний шаблон, который применялся им для поиска или замены.

Если вы не хотите, чтобы Perl выполнял интерполяцию регулярного выражения, в качестве ограничителя надо использовать апостроф (одиночную кавычку) — тогда шаблон будет вести себя, как текстовая строка, заключенная в апострофы. Однако, например, в случае команды замены s/.../.../ с модификатором e или ee (их работа описывается чуть дальше) для второго аргумента будет выполняться интерполяция даже в том случае, если он заключен в апострофы.

Если вы уверены, что при любом обращении к команде поиска или замены шаблон остается неизменным (например, несмотря на интерполяцию, скалярные переменные внутри шаблона не будут менять своего значения), то можно задать модификатор o (см. выше раздел «Модификаторы команд m/.../ и s/.../.../»). Тогда Perl компилирует шаблон в свое внутреннее представление только при первой встрече с данной командой поиска или замены. При остальных обращениях к команде будет использовать откомпилированное значение. Однако, если внезапно изменить значение переменных, задействованных в шаблоне, Perl этого даже не заметит.

Команда замены s/.../.../ использует регулярное выражение, указанное в качестве второго аргумента, для замены текста. Поскольку оно обрабатывается (интерполируется) после того, как выполнена очередная операция поиска, в нем можно, в частности, использовать временные переменные, созданные на этапе поиска. В следующем примере мы последовательно заменим местами пары слов, заданных во входном тексте, оставив между ними по одному пробелу:

$text = "One Two Three Four Five Six";

$text =~ s/(\w+)\s*(\w+)/$2 $1 /g;

Two One Four Three Six Five

Однако, Perl допускает и более сложные способы определения заменяющего текста. Так, если для команды s/.../.../ указать модификатор e, то в качестве второго аргумента надо указать код, который необходимо выполнить (например, вызвать функцию). Полученное выражение будет использовано как текст для подстановки. При этом после вычисления текстового значения, но перед его подстановкой будет выполнен процесс интерполяции, аналогичный процессу интерполяции текстовых строк, заключенных в двойные кавычки (см. раздел «Подстановка переменных (интерполяция строк)» в главе 2).

Еще более сложная схема реализуется, если задан модификатор ee. В этом случае второй аргумент команды s/.../.../ — это строковое выражение, которое сперва надо вычислить (то есть интерполировать), затем выполнить в качестве кода (вызвав встроенную функцию Perl eval), и только после второй интерполяции полученный результат подставляется вместо найденного текста.

Работа команды m/.../ в режиме однократного поиска

В скалярном контексте и без модификатора g команда m/.../ возвращает логическое значение — целое число 1 (истина (true)), если поиск оказался успешным, и пустую строку "" (ложь (false)), если нужный фрагмент текста найти не удалось. Если внутри шаблона имеются группы элементов, заключенные в круглые скобки, то после операции поиска создаются нумерованные переменные $1, $2, ..., в которых содержится текст, соответствующий круглым скобкам. В частности, если весь шаблон заключить в круглые скобки, то в случае успешного поиска переменная $1 будет содержать текст, соотнесенный с шаблоном. (После успешного поиска можно также использовать специальные переменные $&, $`, $’ и $+ — см. ранее раздел «Ссылки на найденный текст».) Пример:

$text = "---one---two---three---";

$scalar = ($text =~ m/(\w+)/);

print "Result: $scalar ($1).";

Result: 1 (one).

Если вы используете команду m/.../ в списковом контексте, то возвращаемое значение сильно зависит от того, есть ли группы из круглых скобок в вашем шаблоне. Если они есть (то есть если создаются нумерованные переменные), то после успешного поиска в качестве результата будет получен список, составленный из нумерованных переменных ($1, $2, ...):

$text = "---one, two, three---";

@array = ($text =~ m/(\w+),\s+(\w+),\s+(\w+)/);

print join ("=", @array);

one=two=three.

В отличие от ранних версий, Perl 5 присваивает значения нумерованным переменным, даже если команда поиска работает в списковом контексте:

$text = "---one, two, three---";

($Fa, $Fb, $Fc) = ($text =~ m/(\w+),\s+(\w+),\s+(\w+)/);

print "/$Fa/$Fb/$Fc/\n";

print "$1=$2=$3.\n";

/one/two/three/

one=two=three.

Если же в шаблоне нет групп, выделенных круглыми скобками, то в случае успешного поиска возвращается список, состоящий из одного элемента — числа 1. При неудачном поиске независимо от того, были ли в шаблоне круглые скобки, возвращается пустой список:

$text = "---one, two, three---";

@array = ($text =~ m/z\w+/);

print "Result: /", @array, "/\n";

print "Size: ", $#array+1, ".\n";

Result://

Size: 0.

(Обратите внимание на разницу между пустым и неопределенным списками.)

Работа команды m/.../ в режиме глобального поиска

Команда m/.../ работает иначе, если указан модификатор g, задающий глобальный поиск всех вхождений шаблона по всему тексту. Если оператор используется в списковом контексте и в шаблоне есть группы круглых скобок, то в случае удачного поиска возвращается список, состоящий из всех найденных групп, расположенных друг за другом:

$text = "---one---two---three---";

@array = ($text =~ m/(-(\w+))/);

print "Single: [", join(", ", @array),"].\n";

@array = ($text =~ m/(-(\w+))/g);

print "Global: [", join(", ", @array), "].\n";

Single: [-one, one].

Global: [-one, one, -two, two, -three, three].

Если же в шаблоне нет групп круглых скобок, то оператор поиска возвращает список всех найденных прототипов шаблона — то есть ведет себя так, как если бы весь шаблон был заключен в круглые скобки:

$text = "---one---two---three---";

@array = ($text =~ m/\w+/);

print "Result: (", join(", ", @array), ").\n";

Result: (one, two, three).

В случае неудачного поиска, как и в предыдущих вариантах, возвращается пустой список.

В скалярном контексте и с модификатором g команда m/.../ ведет себя совершенно особым образом. Специальная переменная $_ или переменная, стоящая слева от оператора =~ или !~, при поиске с модификатором g получает дополнительные свойства — в нее записывается последнее состояние. При каждом последующем обращении к данному фрагменту кода поиск будет продолжаться с того места, на котором он остановился в последний раз. Например, следующая команда подсчитывает количество букв x в заданной строке текста:

$text = "Here is texxxxxt.";

$counter = 0;

while ($text =~ m/x/g) {

print "Found another x.\n";

$conter++;

}

print "Total amount = $counter.\n";

Found another x.

Found another x.

Found another x.

Found another x.

Found another x.

Total amount = 5.

Состояние (точнее, позиция) поиска сохраняется даже в случае перехода к следующему оператору поиска, имеющему модификатор g. Неудачный поиск сбрасывает значение в исходное состояние, если только для команды m/.../ не указан модификатор с (то есть, команда должна иметь вид m/.../gс). Изменение текстового буфера, для которого выполняется поиск, также сбрасывает позицию поиска в исходное состояние. В следующем примере из текстовой строки последовательно извлекаются и выводятся пары имя/значение до тех пор, пока строка не закончится:

$text = "X=5; z117e=3.1416; temp=1024;";

$docycle = 1; $counter = 0;

while ($docycle) {

undef $name; undef $value;

if ($text =~ m/(\w+)\s*=\s*/g) {$name = $1;}

if ($text =~ m/([\d\.\+\-]*)\s*;/g) {$value = $1;}

if (defined($name) and defined($value)) {

print "Name=$name, Value=$value.\n";

$counter++;

} else {

$docycle = 0;

}

}

print "I have found $conter values.\n";

Name=X, Value=5.

Name=z117e, Value=3.1416.

Name=temp, Value=1024.

I have found 3 values.

Позиция, на которой остановился поиск, может быть прочитана и даже переустановлена с помощью встроенной функции Perl pos. В шаблоне на текущую позицию поиска можно ссылаться с помощью метасимвола \G. В следующем примере из строки последовательно извлекаются буквы p, o и q и выводится текущая позиция поиска:

$index = 0;

$_ = "ppooqppqq";

while ($index++ < 2) {

print "1: ’";

print $1 while /(o)/gc; print "’, pos=", pos, "\n";

print "2: ’";

print $1 if /\G(q)/gc; print "’, pos=", pos, "\n";

print "3: ’";

print $1 while /(p)/gc; print "’, pos=", pos, "\n";

}

1: ’oo’, pos=4

2: ’q’, pos=5

3: ’pp’, pos=7

1: ’’, pos=7

2: ’q’, pos=8

3: ’’, pos=8

В документации Perl приводится основанный на этом механизме интересный пример последовательного лексического разбора текста. В нем каждая последующая команда поиска очередной лексической единицы начинает выполняться с того места, где завершила свою работу предыдущая. Советую внимательно разобраться с этим примером (страница руководства perlop, раздел «Regexp Quote-Like Operators», описание команды m/PATTERN/), если вы хотите расширить доступный вам инструментарий Perl!

Замена строк с помощью команды tr/.../.../

Кроме команд m/.../ и s/.../.../, строки можно обрабатывать с помощью команды tr/.../.../ (она же — команда y/.../.../):

tr/список1/список2/модификаторы

y/список1/список2/модификаторы

В отличие от m/.../ и s/.../.../, эта команда не использует шаблоны и регулярные выражения, а выполняет посимвольную замену, подставляя в текст вместо литер из первого списка соответствующие им литеры из второго списка. Например, в следующем случае производится замена литер «i» на «o»:

$text = "My name is Tim.";

$text =~ tr/i/o/;

print $text;

My name is Tom.

В качестве списков используются идущие друг за другом символы, не разделяемые запятыми (то есть это скорее строки, чем списки). В отличие от шаблонов команд m/.../ и s/.../.../, аргументы команды tr/.../.../ не интерполируются (то есть подстановки значений вместо имен переменных не происходит), хотя escape-последовательности, указанные внутри аргументов, обрабатываются правильно.

Подобно m/.../ и s/.../.../, команда tr/.../.../ по умолчанию работает с переменной $_:

while (<>) {

tr/iI/jJ/;

print;

}

В качестве списков можно указывать диапазоны символов — как, например, в следующем фрагменте кода, заменяющем строчные буквы на заглавные:

$text = "Here is the text.";

$text =~ tr/a-z/A-Z/;

print $text;

HERE IS THE TEXT.

Как и в случае m/.../ и s/.../.../, команда tr/.../.../ не требует использовать именно знаки косой черты в качестве ограничителей. Можно использовать практически любой символ, отличный от «пробельных», букв и цифр, а также парные скобочные конструкции, описанные ранее в разделе «Особенности работы команд m/.../ и s/.../.../».

Команда tr/.../.../ возвращает число успешных замен. В частности, если не было сделано никаких замен, она возвращает число ноль. Это позволяет, например, подсчитать с помощью команды tr/.../.../ количество вхождений буквы x в строку $text, не меняя содержимого этой переменной:

$text = "Here is the text.";

$xcount = ($text =~ tr/x/x/);

print $xcount;

1

Если у команды tr/.../.../ нет модификаторов (см. далее раздел «Модификаторы команды tr/.../.../»), то ее аргументы при обычных условиях должны быть одинаковой длины. Если второй аргумент длиннее первого, то он усекается до длины первого аргумента — так, команда tr/abc/0-9/ эквивалентна команде tr/abc/012/. Если первый аргумент длиннее второго и второй не пуст, то для второго аргумента необходимое число раз повторяется его последний символ — так, команда tr/0-9/abc/ эквивалентна команде tr/0123456789/abcccccccc/. Если же второй аргумент пуст, то команда tr/.../.../ подставляет вместо него первый аргумент.

Как легко заметить, если второй аргумент пуст, то (при отсутствии модификаторов) команда tr/.../.../ не производит никаких действий, а возвращаемое ею значение равно числу совпадений между первым аргументом и обрабатываемым текстом. Например, следующая команда подсчитывает количество цифр в строке:

$text = "Pi=3.1415926536, e=2.7182";

$digit_counter=($text =~ tr/0-9//);

print $digit_counter;

16

Команда tr/.../.../ работает без рекурсии, просто последовательно заменяет символы входного текста. Например, для замены заглавных букв на строчные и наоборот, достаточно выполнить команду:

$text = "MS Windows 95/98/NT";

$text =~ tr/A-Za-z/a-zA_Z/;

print $text;

ms wINDOWS 95/98/nt

Если в списке, указанном в качестве первого аргумента, есть повторяющиеся символы, то для замены используется первое вхождение символа:

$text = "Billy Gates";

$text =~ tr/ttt/mvd/;

print $text;

Billy Games

Модификаторы команды tr/.../.../

Команда tr/.../.../ допускает использование следующих модификаторов:

d — Удаляет непарные символы, не выравнивая аргументы по длине.

c — В качестве первого аргумента использует полный список из 256 символов за вычетом указанных в списке символов.

s — Удаляет образовавшиеся в результате замены повторяющиеся символы.

Если указан модификатор d, а первый аргумент команды длиннее второго, то все символы из первого списка, не имеющие соответствия со вторым списком, удаляются из обрабатываемого текста. Пример: удаляем строчные латинские буквы и заменяем пробелы на слеши:

$text = "Here is the text.";

$text =~ tr[ a-z][/]d;

print $text;

H///.

(Наличие модификатора d — единственный случай, когда первый и второй аргументы не выравниваются друг относительно друга. В остальных вариантах второй аргумент либо усекается, либо последний символ в нем повторяется до тех пор, пока аргументы не сравняются, либо, если второй аргумент пуст, вместо второго аргумента берется копия первого.)

Если указан модификатор c, то в качестве первого аргумента рассматриваются все символы, кроме указанных. Например, заменим на звездочки все символы, кроме строчных латинских букв:

$text = "Here is the text.";

$text =~ tr/a-z/*/c;

print $text;

*ere*is*the*text*

Если указан модификатор s, то в случае, если замещаемые символы образуют цепочки из одинаковых символов, они сокращаются до одного. Например, заменим слова, состоящие из латинских букв, на однократные символы косой черты:

$text = "Here is the text.";

$text =~ tr(A-Za-z)(/)s;

print $text;

/ / / /.

Без модификатора результат был бы другим:

$text = "Here is the text.";

$text =~ tr(A-Za-z)(/);

print $text;

//// // /// ////.

Примеры:

1. Заменить множественные пробелы и нетекстовые символы на одиночные пробелы:

$text = "Here is the text.";

$text =~ tr[\000-\040\177\377][\040]s;

print $text;

Here is the text.

2. Сократить удвоенные, утроенные и т.д. буквы:

$text = "Here is the texxxxxxt.";

$text =~ tr/a-zA-Z//s;

print $text;

Here is the text.

3. Пересчитать количество небуквенных символов:

$xcount=($text =~ tr/A-Za-z//c);

4. Обнулить восьмой бит символов, удалить нетекстовые символы:

$text =~ tr{\200-\377}{\000-\177};

$text =~ tr[\000-\037\177][]d;

5. Заменить нетекстовые и 8-ми битные символы на одиночный пробел:

$text =~ tr/\021-\176/ /cs;

Поиск отдельных слов

Чтобы выделить слово, можно использовать метасимвол \S, соответствущий символам, отличным от «пробельных»:

$text = "Now is the time.";

$text =~ /(\S+)/;

print $1;

Now

Однако метасимвол \S соответствует также и символам, обычно не используемым для идентификаторов. Чтобы отобрать слова, составленные из латинских букв, цифр и символов подчеркивания, нужно использовать метасимвол \w:

$text = "Now is the time.";

$text =~ /(\w+)/;

print $1;

Now

Если требуется включить в поиск только латинские буквы, надо использовать класс символов:

$text = "Now is the time.";

$text =~ /([A-Za-z]+)/;

print $1;

Now

Более безопасный метод состоит в том, чтобы включить в шаблон мнимые символы границы слова:

$text = "Now is the time.";

$text =~ /\b([A-Za-z]+)\b/;

print $1;

Now

Привязка к началу строки

Началу строки соответствует метасимвол (мнимый символ) ^. Чтобы привязать шаблон к началу строки, надо задать этот символ в начале регулярного выражения. Например, вот так можно проверить, что текст не начинается с точки:

$line = ".Hello!";

if ($line =~ m/^\./) {

print "Shouldn’t start a sentence with a period!\n";

}

Shouldn’t start a sentence with a period!

Подсказка: Чтобы точка, указанная в шаблоне, не интерпретировалась как метасимвол, перед ней пришлось поставить обратную косую черту.

Привязка к концу строки

Чтобы привязать шаблон к концу строки, используется метасимвол (мнимый символ) $. В нашем примере мы используем привязку шаблона к началу и к концу строки, чтобы убедиться, что пользователь ввел только слово «exit»:

while (<>) {

if (m/^exit$/) {exit;}

}

Поиск чисел

Для проверки того, действительно ли пользователь ввел число, можно использовать метасимволы \d и \D. Метасимвол \D соответствует любому символу, кроме цифр. Например, следующий код проверяет, действительно ли введенный текст представляет собой целое значение без знака и паразитных пробелов:

$text = "Hello!";

if ($text =~ /\D/) {

print "It is not a number.\n";

}

It is not a number.

То же самое можно проделать, использовав метасимвол \d:

$text = "333";

if ($text =~ /^\d+$/) {

print "It is a number.\n";

}

It is a number.

Вы можете потребовать, чтобы число соответствовало привычному формату. То есть, число может содержать десятичную точку, перед которой стоит по крайней мере одна цифра и, возможно, какие-то цифры после нее:

$text = "3.1415926";

if ($text =~ /^(\d+\.\d*|\d+)$/) {

print "It is a number.\n";

}

It is a number.

Подсказка: Чтобы точка, указанная в шаблоне, не интерпретировалась как метасимвол, не забудьте поставить перед ней обратную косую черту.

Кроме того, при проверке можно учитывать тот факт, что перед числом может стоять как плюс так и минус (или пустое место):

$text = "-2.7182";

if ($text =~ /^([+-]*\d+(\.\d*|)$/) {

print "It is a number.\n";

}

It is a number.

Подсказка: Поскольку «плюс» является метасимволом, его надо защищать обратной косой чертой. Однако внутри квадратных скобок, то есть класса символов, он не может быть квантификатором. Знак «минус» внутри класса символов, обычно играет роль оператора диапазона, и поэтому должен защищаться обратной косой чертой. Однако в начале или в конце шаблона он никак не может обозначать диапазон, и поэтому обратная косая черта необязательна.

Наконец, более строгая проверка требует, чтобы знак, если он присутствует, был только один:

$text = "+0.142857142857142857";

if ($text =~ /^(+|-|)\d+(\.\d*|)$/) {

print "It is a number.\n";

}

It is a number.

Подсказка: Альтернативные шаблоны, если они присутствуют, проверяются слева направо. Перебор вариантов обрывается, как только найдено соответствие между текстом и шаблоном. Поэтому, например, порядок альтернатив в шаблоне (\.\d*|) мог бы стать критичным, если бы не привязка к концу строки.

Наконец, вот как можно произвести проверку того, что текст является шестнадцатиричным числом без знака и остальных атрибутов:

$text = "1A0";

unless ($text =~ m/^[a-fA-F\d]+$/) {

print "It is not a hex number.\n";

}

Подсказка: Придумайте сами, как добавить десятичную мантиссу Enn к формату десятичного числа, а также знак и префикс 0x к формату шестнадцатиричного числа.

Проверка идентификаторов

С помощью метасимвола \w можно проверить, состоит ли текст только из букв, цифр и символов подчеркивания (это те символы, которые Perl называет словными (word characters)):

$text = "abc";

if ($text =~ /^\w+$/) {

print "Only word characters found.\n";

}

Only word characters found.

Однако, если вы хотите убедиться, что текст содержит латинские буквы и не содержит цифр или символов подчеркивания, придется использовать другой шаблон:

$text = "aBc";

if ($text =~ /^[A-Za-z]+$/) {

print "Only letter characters found.\n";

}

Only letter characters found.

Наконец, для проверки, что текст является идентификатором, то есть начинается с буквы и содержит буквы, цифры и символы подчеркивания, можно использовать команду:

$text = "X125c";

if ($text =~ /^[A-Za-z]\w+$/) {

print "This is identifier.\n";

}

This is identifier.

Подсказка: Придумайте, как исключить из символов, используемых для построения идентификаторов, знак подчеркивания.

Как найти множественные совпадения

Для поиска нескольких вхождений шаблона можно использовать модификатор g. Следующий пример, который мы уже видели ранее, использует команду m/.../.../ с модификатором g для поиска всех вхождений буквы x в тексте:

$text = "Here is texxxxxt.";

while ($text =~ m/x/g) {

print "Found another x.\n";

}

Found another x.

Found another x.

Found another x.

Found another x.

Found another x.

Модификатор g делает поиск глобальным. В данном (скалярном) контексте Perl помнит, где он остановился в строке при предыдущем поиске. Следующий поиск продолжается с отложенной точки. Без модификатора g команда m/.../ будет упорно находить первое вхождение буквы x, и цикл будет продолжаться бесконечно.

В отличие от команды m/.../, команда s/.../.../ с модификатором g выполняет глобальную замену за один раз, работая так, будто внутри нее уже имеется встроенный цикл поиска, подобный приведенному выше. Следующий пример за один раз заменяет все вхождения x на z:

$text = "Here is texxxxxt.";

$text =~ s/x/z/g;

print $text;

}

Here is tezzzzzt.

Без модификатора g команда s/.../.../ заменит только первую букву x.

Команда s/.../.../ возвращает в качестве значения число сделанных подстановок, что может оказаться полезным:

$text = "Here is texxxxxt.";

print (text =~ s/x/z/g)

5

Поиск нечувствительных к регистру совпадений

Вы можете использовать модификатор i, чтобы сделать поиск нечувствительным к разнице между заглавными и строчными буквами. В следующем примере программа повторяет на экране введенный пользователем текст до тех пор, пока не будет введено Q или q (сокращение для QUIT или quit), после чего программа прекращает работу:

while (<>) {

chomp;

unless (/^q$/i) {

print;

} else {

exit;

}

}

Выделение подстроки

Чтобы получить найденную подстроку текста, можно использовать круглые скобки в теле шаблона. (Если это более удобно, можно также использовать встроенную функцию substr.) В следующем примере мы вырезаем из текстовой строки нужный нам тип изделия:

$record = "Product number:12345

Product type: printer

Product price: $325";

if ($record =~ /Product type:\s*([a-z]+)/i) {

print "The product’s type is $1.\n";

}

The product’s type is printer.

Вызов функций и вычисление выражений при подстановке текста

Используя для команды s/.../.../ модификатор e, вы то тем самым показываете, что правый операнд (то есть подставляемый текст) — это то выражение Perl, которое надо вычислить. Например, с помощью встроенной функции Perl uc (uppercase) можно заменить все строчные буквы слов строки на заглавные:

$text = "Now is the time.";

$text =~ s/(\w+)/uc($1)/ge;

print $text;

NOW IS THE TIME.

Вместо функции uc($1) можно поместить произвольный код, включая вызовы подпрограмм.

Поиск n-го совпадения

С помощью модификатора g перебраются все вхождения заданного шаблона. Но что делать, если нужна вполне определенная точка совпадения с шаблоном — например, вторая или третья? Оператор цикла while в сочетании с круглыми скобками, выделяющими нужный образец, поможет вам:

$text = "Name:Anne Name:Burkart Name:Claire Name:Dan";

$match = 0;

while ($text =~ /Name:\s*(\w+)/g) {

++$match;

print "Match number $match is $1.\n";

}

Match number 1 is Anne

Match number 2 is Burkart

Match number 3 is Claire

Match number 4 is Dan

Этот пример можно переписать, используя цикл for:

$text = "Name:Anne Name:Burkart Name:Claire Name:Dan";

for ($match = 0;

$text =~ /Name:\s*(\w+)/g;

print "Match number ${\++$match} is $1.\n")

{}

Match number 1 is Anne

Match number 2 is Burkart

Match number 3 is Claire

Match number 4 is Dan

Если же вам требуется определить нужное совпадение не по номеру, а по содержанию (например, по первой букве имени пользователя), то вместо счетчика $match можно анализировать содержимое переменной $1, обновляемой при каждом найденном совпадении.

Когда требуется не найти, а заменить второе или третье вхождение текста, можно использовать ту же схему, использовав в качестве тела цикла выражение Perl, вызываемое для вычисления заменяющей строки:

$text = "Name:Anne Name:Burkart Name:Claire Name:Dan";

$match = 0;

$text =~ s/(Name:\s*(\w+))/ ### начинается код Perl

if (++$match == 2) # увеличить счетчик

{"Name:John ($2)"} # вернуть новое значение

else {$1} # оставить старое значение

/gex;

print $text;

Name:Anne Name:John (Burkart) Name:Claire Name:Dan

В процессе глобального поиска при каждом найденном совпадении вычисляется выражение, указанное в качестве второго операнда. При его вычислении увеличивается значение счетчика, и в зависимости от него в качестве замены подставляется либо старое значение текста, либо новое. Модификатор x позволяет добавить в поле шаблона комментарии, делая код более прозрачным. (Обратите внимание, что нам пришлось заключить весь шаблон в круглые скобки, чтобы получить значение найденного текста и подставить его на прежнее место полностью.)

Как ограничить «жадность» квантификаторов

По умолчанию квантификаторы ведут себя как «жадные» объекты. Начиная с текущей позиции поиска они захватывают самую длинную строку, которой может соответствовать регулярное выражение, стоящее перед квантификатором. Алгоритм перебора с возвратами, используемый Perl, способен ограничивать аппетит квантификаторов, возвращаясь назад и уменьшая длину захваченной строки, если не удалось найти соответствия между текстом и шаблоном (детали см. в подразделе «“Жадность” квантификаторов» раздела «Квантификаторы в регулярных выражениях»). Однако этот механизм не всегда работает так, как хотелось бы.

Рассмотрим следующий пример. Мы хотим заменить текст «That is» текстом «That’s». Однако в силу «жадности» квантификатора регулярное выражение «.*is» сопоставляется фрагменту текста от начала строки и до последнего найденного «is»:

$text = "That is some text, isn’t it?";

$text =~ s/.*is/That’s/;

print $texts;

That’sn’t it?

Чтобы сделать квантификаторы не столь жадными — а именно, заставить их захватывать минимальную строку, с которой сопоставимо регулярное выражение, — после квантификатора нужно поставить вопросительный знак. Тем самым квантификаторы, перечисленные в разделе «Квантификаторы в регулярных выражениях», принимают следующий вид:

— ноль или несколько совпадений,

— одно или несколько совпадений,

— ноль совпадений или одно совпадение,

— ровно n совпадений,

— по крайней мере n совпадений,

— совпадений по крайней мере n, но не более, чем m.

Обратите внимание, что смысл квантификатора от этого не меняется, меняется только поведение алгоритма поиска. Если в процессе сопоставления шаблона и текста прототип определяется однозначно, то алгоритм поиска с возвратами увеличит «жадность» такого квантификатора точно также, как он ограничивает аппетит его собрата. Однако, если выбор неоднозначен, то результат поиска будет другим:

$text = "That is some text, isn’t it?";

$text =~ s/.*?is/That’s/;

print $texts;

That’s some text, isn’t it?

Как удалить ведущие и завершающие пробелы

Чтобы отсечь от строки начальные «пробельные символы», можно использовать следующую команду:

$text = " Now is the time.";

$text =~ s/^\s+//;

print $texts;

Now is the time.

Чтобы отсечь «хвостовые» пробелы, годится команда:

$text = "Now is the time. ";

$text =~ s/\s+$//;

print $texts;

Now is the time.

Чтобы отсечь и начальные, и хвостовые пробелы, лучше вызвать последовательно эти две команды, чем использовать шаблон, делающий отсечение ненужных пробелов за один раз. Поскольку процедура сопоставления шаблона и текста достаточно сложна, на эту простую операцию может уйти гораздо больше времени, чем хотелось бы.

Утверждения, проверяющие текст перед шаблоном и после него

Как уже упоминалось, в регулярных выражениях Perl есть конструкции (мнимые символы нулевой длины), позволяющие проверять утверждения о фрагментах текста, расположенных перед проверяемым шаблоном или после него:

— после текущей точки находится фрагмент текста, соответствующий указанному регулярному выражению,

— после текущей точки нет текста, соответствующего указанному регулярному выражению,

— перед текущей точкой есть фрагмент текста, соответствующий указанному регулярному выражению,

— перед текущей точкой нет фрагмента текста, соответствующего указанному регулярному выражению.

(Конструкции (?<=шаблон) и (?<!шаблон) работают только с шаблонами, соответствующими фиксированному числу символов. Иными словами, в шаблонах, указываемых для (?<=...) и (?<!....), не должно быть квантификаторов.)

Эти условия полезны, если нужно проверить, что перед определенным фрагментом текста или после него находится нужная строка, однако ее не требуется включать в результат поиска. Это бывает необходимо, если в коде используются специальные переменные $& (фрагмент, для которого найдено соответствие между текстом и регулярным выражением), $` (текст, предшествующий найденному фрагменту) и $’ (текст, следующий за найденным фрагментом). (Более гибким представляется применение нумерованных переменных $1, $2, $3, ..., в которые заносятся отдельные части найденного фрагмента.)

В следующем примере ищется слово, за которым следует пробел, но сам пробел не включается в результат поиска:

$text = "Mary Tom Frank ";

while ($text =~ /\w+(?=\s)/g) {

print $& . "\n";

Mary

Tom

Frank

Того же результата можно добиться, если заключить в круглые скобки интересующую нас часть шаблона и затем использовать ее как переменную $1:

$text = "Mary Tom Frank ";

while ($text =~ /(\w+)\s/g) {

print $1 . "\n";

Mary

Tom

Frank

Следует четко понимать, что вы имеете в виду, когда используете то или иное условие. Рассмотрим следующий пример:

$text = "Mary+Tom ";

if ($text =~ m|(?!Mary\+)Tom|) {

print "Tom is without Mary!\n";

} else {

print "Tom is busy...\n";

}

Вопреки нашим ожиданиям, Perl напечатает:

$text = "Mary+Tom ";

if ($text =~ m|(?!Mary\+)Tom|) {

print "Tom is without Mary!\n";

} else {

print "Tom is busy...\n";

}

Tom is without Mary!

Это произойдет по следующей причине. Пробуя различные начальные точки входной строки, от которой начинается сопоставление шаблона и текста, Perl рано или поздно доберется до позиции, расположенной прямо перед именем «Tom». Условие (?!Mary\+) требует, чтобы после текущей точки не находился текст «Mary+», и это условие для рассматриваемой точки будет выполнено. Далее, Perl последовательно проверяет, что после текущей точки следуют буквы «T», «o» и «m», и это требование также в силе (после проверки условия (?!Mary\+) текущая точка остается на месте). Тем самым найдено соответствие между подстрокой «Tom» и шаблоном, поэтому команда поиска возвращает значение истина.

Регулярное выражение (?!Mary\+)....Tom, резервирующее четыре символа под текст «Mary+», для приведенного выше случая выведет то, что требовалось, но выдаст ошибочный ответ, если перед именем «Tom» нет четырех символов:

$text = "O, Tom! ";

if ($text =~ m|(?!Mary\+)....Tom|) {

print "Tom is without Mary!\n";

} else {

print "Tom is busy...\n";

}

Tom is busy...

Наконец, если более точно сформулировать, чего требуется, получится нужный результат:

$text = "Mary+Tom ";

if ($text =~ m|(?<!Mary\+)Tom|) {

print "Tom is without Mary!\n";

} else {

print "Tom is busy...\n";

}

Tom is busy...

Глава 7. Подпрограммы

Коротко

Работа с подпрограммами

Идея подпрограммы по сути есть воплощение старого лозунга программистов: разделяй и властвуй. Подпрограммы позволяют разбить код на части обозримого размера, что несколько упрощает процесс программирования. Предположим, например, требуется напечатать два числа, выводя для чисел, которые меньше десяти, сообщение об ошибке:

$value = 10;

if ($value > 10) {

print "Value is $value.\n";

} else {

print "Value is too small.\n";

}

$value = 12;

if ($value > 10) {

print "Value is $value.\n";

} else {

print "Value is too small.\n";

}

Value is too small.

Value is 12.

Однако, можно сделать лучше — сэкономить место, упаковав повторяющиеся блоки условных операторов в виде подпрограммы. Назовем такую подпрограмму, например, printifOK:

sub printifOK

{

my $internalvalue = shift(@_);

if ($internalvalue > 10) {

print "Value is $internalvalue.\n";

} else {

print "Value is too small.\n";

}

}

Переданные подпрограмме параметры запоминаются, а потом по мере надобности извлекаются из специального массива @_. Весь оставшийся код — это уже знакомый нам условный оператор. Чтобы использовать подпрограмму, ей надо передать требуемые значения. Результат, конечно, получается ровно таким же, как и в предыдущем примере.

$value = 10;

printifOK($value);

$value = 12;

printifOK($value);

Value is too small.

Value is 12.

Подпрограммы могут также возвращать значения. Так в следующем случае, с помощью команды return, возвращается сумма двух переданных величин:

sub addem

{

($value1, $value2) = @_;

return $value1 + $value2;

}

print "2 + 2 = " . addem(2,2) . "\т"ж

2 + 2 = 4

Подсказка: В других языках программирования функции поддерживаются отдельно от подпрограмм. Но в Perl подпрограммы могут возвращать значения, и для роли функций не вводятся подпрограммы специального типа. Тем самым слова «функция» и «подпрограмма» в Perl являются синонимами.

Вот вкратце как работают подпрограммы — они позволяют разбивать код на полуавтономные куски, которым передаются одни данные, а обратно возвращаются другие. Такая разбивка кода делает программы более легкими для написания и сопровождения. Зачастую это еще и сильно сокращает размер программ. Теперь рассмотрим все более детально.

Непосредственные решения

Объявление подпрограмм

Объявление можно использовать, чтобы сообщить Perl о существовании подпрограммы, и уточнить при этом типы передаваемых аргументов и возвращаемого значения. Объявление (declaration) подпрограммы отличается от определения (definition) —при определении задается код, составляющий тело подпрограммы.

В отличие от других языков программирования, в Perl перед использованием объявлять подпрограммы не надо. Исключением является вызов подпрограмм (функций) без круглых скобок, охватывающих список параметров (например, списочных операторов). В этом случае перед тем, как использовать подпрограмму, необходимо ее объявить.

Подпрограмма может быть объявлена одним из следующих способов:

sub имя;

sub имя (прототип);

sub имя {блок};

sub имя (прототип) {блок};

(в последних двух случаях вместе с описанием подпрограммы производится ее определение, то есть задается код, составляющий тело подпрограммы).

При объявлении подпрограммы можно указать ее прототип — по нему Perl определяет параметры подпрограммы. Некоторые программисты предпочитают использовать прототипы, как способ проверки правильности кода, — более подробно об этом рассказывается в следующем разделе «Использование прототипов».

Разрешается импортировать подпрограммы из пакетов Perl:

use пакет qw(имя1 имя2 имя3);

(Использование псевдокавычек qw/.../ (см. таблицу 2.3 в главе 2) — самый простой способ создать список из строк-имен функций, заключенных в кавычки. Более подробно о команде use рассказывается в главе 13.)

Для подпрограмм можно использовать любые имена. Однако, следует помнить, что Perl резервирует имена, составленные целиком из заглавных букв, для неявно вызываемых Perl подпрограмм пакетов типа BEGIN или END.

Чтобы отличать имена подпрограмм от других типов данных перед каждым именем подразумевается символ &. В большинстве случаев его можно опустить — если Perl распознает из контекста, что имеет дело с подпрограммой, он самостоятельно подставляет в начале имени символ &. Например, если вы присвоили своей подпрограмме имя count, то можете вызывать ее как count(1,2) или &count(1,2) (см. раздел «Вызов подпрограмм» далее в этой главе).

Использование прототипов

Некоторые программисты любят использовать прототипы, как средство проверки правильности вызова подпрограмм — например, что в качестве параметра вместо массива не передается скалярная переменная, и т. д. Чтобы объявить прототип, надо перечислить в нужном порядке символы, соответствующие префиксам аргументов: $ для скаляров, @ для массивов, и т. д. (см. таблицу 7.1).

Если в прототипе в качестве формального параметра указаны символы @ или %, то этот параметр поглощает все последующие. То есть, массив или хеш могут быть только последним параметром подпрограммы:

sub NAME ($$@)

Это означает, что при вызове подпрограммы первыми двумя параметрами будут скаляры, за которыми следует список скалярных объектов, образуемый по известным правилам:

NAME $scalar1, $scalar2, $lstarg1, $lstarg2, $lstarg3;

Если вы хотите, чтобы при вызове подпрограммы аргумент и в самом деле начинался с префикса @ или %, а не представлял собой список скалярных аргументов, необходимо защитить эти символы с помощью обратной косой черты:

sub SUBNAME (\@)

Теперь можно вызывать подпрограмму, указывая ей переменную-массив в качестве аргумента:

SUBNAME @array;

При желании можно определить для подпрограммы необязательные параметры. Список необязательных параметров отделяется от обязательных точкой с запятой (см. таблицу 7.1).

Подсказка: Прототипы влияют на интерпретацию вызова подпрограммы, только когда при вызове для имени подпрограммы не указан префикс &.

Таблица 7.1. Как создать прототип подпрограммы

Описание

Вызов подпрограммы

sub имя($)

имя $arg1;

sub имя($$)

имя $arg1, $arg2;

sub имя($$;$)

имя $arg1, $arg2; или

имя $arg1, $arg2, $arg3;

sub имя(@)

имя $arg1, $arg2, $arg3, $arg4;

sub имя(%)

имя $key1 => $val1,

$key2 => $val2;

sub имя($@)

имя $ARG, $arg1, $arg2, $arg3;

sub имя($%)

имя $ARG, $key1 => $val1,

$key2 => $val2;

sub имя(\@)

имя @array;

sub имя(\%)

имя %{$ссылка-на-hash};

sub имя(&)

имя непоименованная-подпрограмма;

sub имя(*)

имя *arg1;

sub имя()

имя;

Определение прототипов

Определение отличается от описания тем, что в нем приводится код, составляющий тело подпрограммы. Чтобы определить подпрограмму, используется ключевое слово sub:

sub имя {блок};

sub имя (прототип) {блок};

Например, требуется задать подпрограмму printhello, просто выводящую сообщение «Hello!» (обратите внимание, что тело подпрограммы заключено в фигурные скобки { и }, хотя и состоит из одной команды):

sub printhello

{

print "Hello!\n";

}

Теперь можно вызвать эту подпрограмму:

printhello;

Hello!

Более подробно процедура вызова рассматривается в следующем разделе «Вызов подпрограмм».

Вызов подпрограмм

Если подпрограмма определена, ее можно вызвать с конкретными аргументами в качестве параметров:

&имя (список-аргументов);

Как всегда в Perl, выполнить вызов подпрограммы можно далеко не одним способом. Например, при использовании круглых скобок не обязательно ставить префикс &:

имя (список-аргументов);

Если подпрограмма была описана или экспортирована ее из пакета то заодно можно опустить и круглые скобки:

имя список-аргументов;

При вызове подпрограммы передаваемые ей аргументы помещаются в специальный массив @_. Если подпрограмма вызывается с префиксом &, но без списка параметров, то в качестве последнего ей передается текущее содержимое массива @_. Это полезно в том случае, когда одна подпрограмма вызывается из другой, причем ей требуется передать те же параметры, которые использовались при вызове первой подпрограммы. (Обратите внимание, что при использовании префикса & Perl не выполняет проверку соответствия прототипа подпрограммы списку фактически передаваемых величин.)

Аргументы, передаваемые подпрограмме, образуют единый унифицированный список. Если передается два массива, их содержимое будет объединено в единый массив и добавлено к списку значений, передаваемых подпрограмме. Если вы хотите передавать в качестве параметров массивы и сохранять их структуру, передавайте их по ссылке (см. раздел «Передача параметров по ссылке» далее в этой главе.)

Чтение аргументов, переданных подпрограмме

Доступ к аргументам, переданным подпрограмме, осуществляется через специальный массив @_, в который заносятся эти аргументы. Например, если переданы два параметра, подпрограмма может обратиться к ним как $_[0] и $_[1].

Предположим, что вы хотите сложить два числа и напечатать результат. Для этой цели создается процедура addem, которую можно вызвать, как addem(2,2). Посмотрим, как addem получает значения через массив @_:

sub addem

{

$value1 = $_[0];

$value2 = $_[1];

print "$value1 + $value2 = " . ($value1+$value2);

}

addem(2,2);

2 + 2 = 4

Чтобы извлечь параметры из массива @_, можно также использовать функцию shift:

sub addem

{

$value1 = shift @_;

$value2 = shift @_;

print "$value1 + $value2 = " . ($value1+$value2);

}

addem(2,2);

2 + 2 = 4

Наконец, в качестве третьего метода, чтобы получить значение параметров за один раз, можно использовать присвоение списком:

sub addem

{

($value1, $value2) = @_;

print "$value1 + $value2 = " . ($value1+$value2);

}

addem(2,2);

2 + 2 = 4

Использование переменного числа параметров

Perl упрощает передачу подпрограмме переменного числа параметров, поскольку они помещаются в массив @_. Чтобы определить, сколько параметров было передано подпрограмме, достаточно проверить длину этого массива (то есть переменную $#_).

Чтобы напрямую работать с аргументами, занесенными в массив @_, можно использовать цикл foreach. Он выполнит полный перебор переданных параметров независимо от того, сколько их было задано:

sub addem

{

$sum = 0;

foreach $element (@_) {;

$sum += $element;

}

print join(" + ", @_) . " = " . $sum . "\n";

}

addem(2, 2, 2);

2 + 2 + 2 = 6

Использование значений по умолчанию

Поскольку процедура может получать переменное число аргументов, иногда требуется обеспечить значения по умолчанию для опущенных параметров. Это можно сделать с помощью оператора ||=. Оператор || («логическое и») во-первых, предпочитает не загружать себя лишней работой и не вычисляет второй аргумент, если уже первый соответствует значению истино. Во-вторых, он возвращает не логические значения ложь и истина, а первый из двух аргументов, который не ложь:

sub addem

{

($value1, $value2) = @_;

$value2 ||= 1;

print "$value1 + $value2 = " . ($value1+$value2);

}

Так как после присвоения списком значение $value2 осталось неопределенным, а с точки зрения Perl неопределенное значение соответствует условию ложь, то скаляру $value2 будет присвоено используемое по умолчанию значение 1:

addem(2);

2 + 1 = 3

Однако, этот метод подразумевает, что в качестве второго параметра не будет задано число ноль или пустая строка. Правильнее осуществить проверку количества элементов массива @_ (это значение $#_) в явном виде. Это позволит определить, сколько же аргументов задано при вызове подпрограммы:

sub addem

{

$value1 = shift @_;

if ($#_ > 0) {

$value2 = shift @_;

} else {

$value2 = 1;

}

print "$value1 + $value2 = " . ($value1+$value2);

}

addem(2);

2 + 1 = 3

Значения, возвращаемые подпрограммами (функциями)

Задать возвращаемое значение и выйти из подпрограммы можно также с помощью команды return<$FЕсли в теле подпрограммы нет команды return, при выходе из подпрограммы возвращается последнее вычисленное значение. — Примеч. ред.>. (В некоторых языках программирования функции возвращают значение, а подпрограммы этого делать не могут. Однако в Perl функция и подпрограмма — это одно и то же.) Существенно: значение, возвращаемое оператором return, вычисляется в том контексте (скалярном или списковом), в котором вызывается подпрограмма.

Например, вот как передать подпрограмме два параметра и вернуть их сумму:

sub addem

{

($value1, $value2) = @_;

return $value1+$value2;

}

print "2 + 2 = " . addem(2, 2) . "\n";

2 + 2 = 4

Точно также вы можете вернуть не одно значение, а их список:

sub getval

{

return 1, 2, 3, 4, 5, 6, 7, 8;

}

Если подпрограмма возвращает список значений, то можно, например, использовать его при присвоении значения массиву:

@array = getvalues;

print join(", ", @array);

1, 2, 3, 4, 5, 6, 7, 8

Однако, если в качестве возвращаемого значения указано несколько списков, массивов или хешей, все они будут упакованы в один большой список. Поэтому значение, возвращаемое подпрограммой, можно присвоить только одному массиву. Например, следующая команда просто не сработает:

(@array1, @array2) = getvalues;

При ее выполнении все значения, возвращаемые подпрограммой getvalues, помещаются в массив @array1, а массив @array2 остается неопределенным. Чтобы справиться с этой проблемой, обратитесь к разделу «Передача параметров по ссылке» далее в этой главе.

Управление областью видимости (ключевые слова my и local)

По умолчанию переменные Perl являются глобальными. Это значит, что вы можете обращаться к ним из любого места программы. (На самом-то деле, они глобальны только в рамках текущего пакета — но о пакетах вы узнаете из главы 15.) Даже если переменная определена внутри тела подпрограммы, она становится глобальной и к ней можно обращаться после выхода из подпрограммы. Например, в следующем фрагменте кода мы легко получаем доступ к переменной $inner, находясь за пределами подпрограммы, в которой она определена и в которой ей присвоено значение:

sub printem

{

$inner = shift @_;

print $inner;

}

printem "Hello!\n";

print "/" . $inner;

Hello!

/Hello!

(как легко видеть, второе «Hello!» получено в результате прямого обращения к переменной, без вызова подпрограммы.)

Если бы на этом все и кончалось, Perl бы быстро стал неуправляемым из-за огромного числа глобальных переменных, мешающих друг другу в процессе работы программы. Однако, можно ограничить переменную пределами подпрограммы, если задать ее область видимости (scope).

Ключевое слово my ограничивает переменную текущим блоком — будь то одиночный блок, условный оператор, оператор цикла, подпрограмма, команда eval или файл, подключаемый командами do, require или use. Переменные, описанные с ключевым словом my, имеют ограниченную лексическую область видимости. В отличие от них переменные, описанные с ключевым словом local, имеют ограниченную динамическую область видимости. Различие между ними состоит в том, что переменные с динамической областью видимости доступны также подпрограммам, которые вызываются из текущего блока. Переменные с лексической областью видимости видны исключительно внутри того блока, в котором они описаны. (Более подробно о динамической области видимости можно узнать из раздела «Создание временных переменных (ключевое слово local)» далее в этой главе.)

Если после ключевого слова my перечислено более одной переменной, список должен быть заключен в круглые скобки. Все элементы списка должны быть допустимыми «левыми значениями» — то есть, их можно указывать в левой части оператора присваивания. Кроме того, лексическую область видимости можно объявлять только для переменных, имена которых составлены из букв, цифр и символа подчеркивания. Тем самым специальные (встроенные) переменные Perl типа $_ объявлять с использованием ключевого слова my нельзя — зато для таких переменных можно задавать ключевое слово local, что позволяет «защитить» значение переменной от изменений, производимых внутри подпрограммы (подробнее см. раздел «Создание временных переменных (ключевое слово local)» далее в этой главе). Наконец, объявление my действительно только для скалярных переменных, массивов и хешей — объявить таким образом, например, подпрограмму, нельзя (в частности потому, что подпрограмма не является «левым значением»).

В следующем примере область видимости переменной$inner ограничивается телом подпрограммы: она объявлена с ключевым словом my. Теперь доступ к ней за пределами подпрограммы запрещен (в частности, команда print выводит пустую строку):

sub printem

{

my $inner = shift @_;

print $inner;

}

printem "Hello!\n";

print "/" . $inner;

Hello!

/

Вот несколько примеров с использованием ключевого слова my:

my $variable1;

my ($variable1, $variable2);

my $variable1 = 5;

Предупреждение: Если вы объявляете с помощью ключевого слова my несколько переменных, то должны заключить их в круглые скобки. Иначе ключевое слово my будет относиться только к первой из перечисленных в списке переменной — как, например, в следующей команде:

my $variable1, $variable2 = 5;

Переменные, объявленные с ключевым словом my, не обязательно находятся внутри блока. Например, если объявление находится в заголовке цикла или условного оператора, то такие переменные приобретают лексическую область видимости в пределах этого оператора. В частности, переменная, объявленная как my в условии условного оператора, имеет лексическую область видимости по отношению к условиям и всем блокам, составляющим оператор:

$testvalue = 10;

if ((my $variable1 = 10) > $testvalue) {

print "Value, ", $variable1,

", is greater than the test value.\n";

} elsif ($variable1 < $testvalue) {

print "Value, ", $variable1,

", is less than the test value.\n";

} else {

print "Value, ", $variable1,

", is equal to the test value.\n";

}

Value, 10, is equal to the test value.

Требование обязательной лексической области видимости

Прагмаuse strict ’vars’ в программе предписывает программисту явно указывать для любой переменной, является ли она локальной или глобальной. То есть, любое упоминание любой переменной, начиная с прагмы и до конца блока или текущей области видимости, должно относиться либо к переменной, описанной с лексической областью видимости, либо к глобальной переменной с явным указанием имени пакета, к которому она относится.

Создание временных переменных (ключевое слово local)

Кроме переменных с лексической областью видимости, создающихся с помощью ключевого слова my, вы можете также образовывать переменные с динамической областью видимости — с помощью ключевого слова local<$FНастоятельно не рекомендуется пользоваться этой возможностью при создании программ на Perl. — Примеч. ред.>. Это ключевое слово создает временную копию глобальной переменной. Внутри динамической области видимости, работа идет с временной копией, и изменения, вносимые в переменную, не сказываются на основной версии переменной. При выходе из динамической области видимости, временная копия уничтожается и глобальная переменная восстанавливает прежнее значение.

Динамическая область видимости отличается от лексической тем, что относится не только к текущему блоку, но и ко всем вызываемым из этого блока подпрограммам. Например, если переменная $_ объявляется как локальная, затем ей присваивается некоторое значение и вызывается команду print без параметров, то print напечатает содержимое временной копии, а не глобальной переменной $_. При выходе из текущего блока временная копия будет уничтожена, и тогда вызовы команды print без параметров будут относиться к «истинной» специальной переменной $_.

Обычно вместо спецификатора local рекомендуется использовать my. Иногда, однако, local позволяет сделать вещи, недоступные для my. Например, может понадобиться сделать временную копию специальной переменной Perl, или изменить отдельный элемент массива или хеш-таблицы, или поработать локально с дескрипторами файлов или форматами Perl. Все эти функции доступны исключительно через спецификатор local.

Обратите внимание, что ключевое слово local создает не новую переменную, а всего лишь копию существующей (как правило) глобальной переменной, с которой вы будете работать в дальнейшем. Вот несколько примеров использования ключевого слова local:

local $variable1;

local ($variable1, $variable2);

local $variable1 = 5;

local *FILEHANDLE;

Итак, описатель local создает копии перечисленных в нем элементов, делает переменные локальными для текущего блока, команды eval, команды do, а также для любой подпрограммы (и всех ее вложенных подпрограмм), которая будет вызвана из этого блока. Как и в случае my, при перечислении более одной переменной, необходимо заключить их в круглые скобки. Все элементы, перечисляемые с local, должны быть правильными «левыми значениями» — то есть, их можно указывать слева от оператора присваивания.

Постоянные (статические) переменные

Иногда надо сделать так, чтобы переменная внутри подпрограммы сохраняла свое значение между вызовами. Однако, если определить переменные с ключевым словом my, то их значения переменных будут сбрасываться при каждом входе в подпрограмму. В следующем примере переменная $count сбрасывается в ноль<$FНа самом деле переменная $count, конечно же, не сбрасывается в ноль, а становится неопределенной. Просто с точки зрения оператора ++ неопределенное значение и ноль — равноправные величины. Вообще, операторы автоприращения и автоуменьшения ++ и -- умеют интерпретировать как арифметические многие входные данные, которые обычные арифметические операторы (+, -, *, /) сочтут за ошибку. (Чуть подробнее об этом свойстве операторов ++ и -- рассказано в главе 4, в разделе «Автоприращение и автоуменьшение».) — Прим. перев.> и следом увеличивается на единицу при каждом входе в подпрограмму, так что на выходе вместо желаемой последовательности натуральных чисел 1, 2, 3, 4 получаются четыре единицы:

sub incrementcount {

my $count;

return ++$count;

}

print incrementcount . "\n";

print incrementcount . "\n";

print incrementcount . "\n";

print incrementcount . "\n";

1

1

1

1

Если б можно было сделать переменную $count статической, как в языке С, это решило бы нашу проблему (статические переменные сохраняют свое значение между вызовами функций). К сожалению, Perl не поддерживает статические переменные напрямую: глобальные переменные являются статическими по умолчанию, а переменные, объявленные внутри подпрограмм, никогда таковыми не бывают.

Однако, существует трюк, с помощью которого эту проблему можно обойти. Переменные с лексической областью видимости не сбрасываются до тех пор, пока они находятся в пределах видимости (точнее, система автоматической сборки мусора не трогает их до тех пор, пока на переменную хоть кто-то ссылается — подпрограмма, ссылка, и т. д.). Поэтому даже если переменная не видна, это еще не значит, что она уничтожена. В следующем примере мы помещаем определение my вне тела подпрограммы, заключив и подпрограмму, и определение переменной в фигурные скобки. В результате $count начинает вести себя как статическая переменная:

{

my $count;

sub incrementcount {

return ++$count;

}

}

print incrementcount . "\n";

print incrementcount . "\n";

print incrementcount . "\n";

print incrementcount . "\n";

1

2

3

4

Здесь использован тот факт, что подпрограмма, даже объявленная внутри блока, является глобальной. Поэтому она доступна в том числе и вне охватывающего ее блока, тогда как локальная переменная $count — нет. При входе в блок в результате выполнения команды my для переменной $count выделяется память, но при выходе из блока память не освобождается — ведь на эту переменную все еще ссылается подпрограмма incrementcount. В результате подпрограмма все равно имеет дело с одной и той же областью памяти, не освобождая ее при выходе и не размещая в ней данных вновь при входе.

Весь этот фрагмент кода можно поместить в блок BEGIN, выполняемый при загрузке программы. Это гарантирует выделение памяти под «постоянные» переменные перед началом работы сценария, а не в процессе его выполнения:

sub BEGIN

{

my $count = 0;

sub incrementcount {

return ++$count;

}

}

print incrementcount . "\n";

print incrementcount . "\n";

print incrementcount . "\n";

print incrementcount . "\n";

1

2

3

4

Обратите внимание, что, когда при входе в блок выделяется память под переменную, но при выходе из блока эта память не освобождается (по причине занятости переменной), при новом входе в блок память не будет повторно выделяться. Как результат, прежнее значение «постоянной» переменной не будет потеряно из-за ее повторной инициализации, при выполнении программы типа:

$flag =1;

LABEL1:

{

my $count = 0;

sub incrementcount {

return ++$count;

}

}

for (1..4) {print incrementcount, "/";}

if ($flag) {

$flag = 0;

goto LABEL1;

}

1/2/3/4/5/6/7/8/

Рекурсивный вызов подпрограмм

В Perl вы можете вызывать подпрограммы рекурсивно — то есть подпрограмма может содержать (прямо или косвенно) вызовы самой себя. Привычный пример рекурсивных операций — это вычисление факториала (например, 5! = 5*4*3*2*1). Не будем отступать от этой традиции.

Мы разбиваем процедуру вычисления факториала на несколько рекурсивных шагов. На каждом шаге значение, переданное в качестве параметра, умножается на результат вычисления факториала с аргументом, уменьшенным на единицу. Только если подпрограмма вызывается с параметром, равным единице, будет возвращено значение единица, без углублений в дальнейшие вычисления:

sub factorial

{

my $value = shift @_;

if ($value == 1) {

return $value;

} else {

return $value * factorial($value-1);

}

}

$result = factorial(6);

print $result;

720

Как видите, эта подпрограмма может вызывать рекурсивно сама себя. (На самом деле, именно это она и делает, если только ее не попросили вычислить факториал единицы.)

Вложенные подпрограммы

В Perl разрешается использовать также и вложенные подпрограммы (то есть, определять подпрограммы внутри подпрограмм). В нашем примере мы определяем подпрограмму outer и подпрограмму inner внутри нее. Обратите внимание, что inner видна не только внутри outer, но и вне ее:

sub outer

{

sub inner

{

print "Inside the inner subroutine.\n";

}

inner;

}

outer;

inner;

Inside the inner subroutine.

Inside the inner subroutine.

Передача параметров по ссылке

Если подпрограмме передаются массивы и хеши, то их элементы объединяются в один список вместе с остальными параметрами. Если вы хотите передать два и более массивов или хешей, сохранив при этом их индивидуальность, то должны передавать ссылки на массивы или хеши (о ссылках подробнее рассказывается в главе 8).

В следующем примере создаются два массива:

@a = (1, 2, 3);

@b = (4, 5, 6);

Мы хотим создать подпрограмму addem, которая складывает попарно элементы двух массивов независимо от их длины. Чтобы добиться этого, мы вызываем подпрограмму addem, передавая ей ссылки на массивы:

@array = addem(\@a, \@b);

Теперь внутри подпрограммы addem надо обратиться к ссылкам на массивы, а затем организовать цикл по числу их элементов, возвращая в качестве результата массив, содержащий попарные суммы элементов входных массивов (этот код станет понятнее после того, как вы прочтете главу 8):

sub addem

{

my ($ref1, $ref2) = @_;

for ($loop_index = 0; $loop_index <= $#{$ref1};

$loop_index++) {

$result[$loop_index] = @{$ref1}[$loop_index] +

@{$ref2}[$loop_index];

}

return @result;

}

Вот как с помощью подпрограммы addem происходит сложение элементов двух массивов:

@array = addem (\@a, \@b);

print join(’, ’, @array);

5, 7, 9

Техника передачи ссылок позволяет непосредственно ссылаться на элементы данных, передаваемые подпрограмме. Это означает, что можно модифицировать параметры, передаваемые подпрограмме. В Perl скаляры и так передаются по ссылке, поэтому для модификации содержимого переменных внутри подпрограммы не требуется передавать на них ссылки в явном виде.

Передача записи таблицы символов (тип данных typeglob)

Передача типа данных typeglob до недавнего времени была единственным способом передать ссылку на переменную средствами Perl. До сих пор это лучший способ передавать в качестве параметров такие данные, как дескрипторы файлов. Поскольку typeglob — это полная запись таблицы символов, то при передаче данных этого типа, реально передаются ссылки на все типы данных, хранящиеся в программе под данным именем. Рассмотрим, как с помощью типа typeglob вместо ссылок реализовать подпрограмму попарного сложения элементов массива, рассмотренную в предыдущем разделе:

@a = (1, 2, 3);

@b = (4, 5, 6);

sub addem

{

local (*array1, *array2) = @_;

for ($loop_index = 0; $loop_index <= $#array1;

$loop_index++) {

$result[$loop_index] = $array1[$loop_index] +

$array2[$loop_index];

}

return @result;

}

@array = addem (\@a, \@b);

print join(’, ’, @array);

5, 7, 9

При передаче дескрипторов файлов в качестве параметра можно передать содержимое записи таблицы символов typeglob — например, в форме *STDOUT. Однако лучше передавать ссылки на записи таблицы символов, потому что такой код будет работать и в том случае, если будет активизирована прагма use strict ’refs’. (Прагмы — это директивы, передаваемые компилятору. Данная прагма проверяет символические ссылки, более подробная информация может быть найдена в главе 8.) В следующем примере мы передаем подпрограмме ссылку на запись *STDOUT таблицы символов:

sub printhello {

my $handle = shift;

print $handle "Hello!\n";

}

printhello (\*STDOUT);

Hello!

Проверка контекста вызова функции: функция wantarray

Подпрограммы могут возвращать как скалярные значения, так и списки. Это значит, что возвращаемое значение может зависеть от контекста, в котором вызвана подпрограмма. Если вы хотите самостоятельно обрабатывать списковый и скалярный контексты, вам нужен инструмент, который скажет, в каком контексте вызывается подпрограмма. Таким инструментом является функция wantarray.

Она возвращает значение истина, если результат работы подпрограммы будет интерпретироваться в списковом контексте, и значение ложь в противном случае. В примере ниже с помощью функции wantarray проверяется контекст вызова функцииswapxy, и в зависимости от контекста возвращается скаляр или список (функция swapxy заменяет в текстовых строках все вхождения латинской буквы «x» на латинскую «y»):

sub swapxy {

my @data = @_;

for (@data) {

s/x/y/g;

}

return wantarray ? @data : $data[0];

}

А вот как выглядит вызов функции swapxy в списковом контексте, когда мы передаем ей на вход список текстовых строк и ожидаем получить обработанный список:

$a = "xyz";

$b = "xxx";

($a, $b) = swapxy($a, $b);

print "$a\n";

print "$b\n";

yyz

yyy

Создание встраиваемых функций

Если для функции задан прототип (), она может встраиваться в код компилятором Perl. Встраиваемая (inline) функция оптимизируется для б’ольшей скорости вычислений, однако она должна подчиняться специальным ограничениям и состоять из константы или скаляра с лексической областью видимости. (В последнем случае на скаляр не должно быть никаких дополнительных ссылок.) Кроме того, на встраиваемую функцию нельзя ссылаться, указывая префикс & или ключевое слово do — такие вызовы функций никогда не преобразуются Perl во встраиваемые.

Например, вот такие функции будут встраиваться Perl:

sub e () {2.71828}

sub e () {exp 1.0}

Обе возвращают число e — основание натурального логарифма. Первая функция задает его в виде константы, а вторая использует встроенную функцию Perl exp. (Кстати, функция exp возвращает более точное значение для основания натурального логарифма, чем константа, указанная в первой функции.)

Замещение встроенных функций. Псевдопакет CORE

Под замещением подпрограммы понимают новое определение существующей подпрограммы. Разрешается замещать любые подпрограммы, в том числе и встроенные в Perl, но только в том случае, если подпрограмма уже импортирована из модуля. В противном случае объявления подпрограммы будет недостаточно.

Однако, для объявления подпрограммы, которая замещает впоследствии импортируемую подпрограмму, вы можете использовать прагму subs. Подпрограммы, чьи имена перечислены в subs, будут замещать встроенные функции Perl. Рассмотрим пример, в котором мы замещаем функцию Perl exit, заставляя ее спрашивать, действительно ли пользователь хочет выйти из программы:

use subs ’exit’;

sub exit

{

print "Do you really want to exit?";

$answer = <>;

if ($answer =~ /^y/i) (CORE::exit;}

}

while (1) {

exit;

}

Здесь для того, чтобы на самом деле выйти из программы, когда пользователь захочет это сделать, мы используем псевдопакет CORE, вызывая функцию CORE::exit. (В Perl можно указывать в явном виде, из какого пакета берется та или иная функция, используя символы :: в качестве разделителя.) Псевдопакет CORE содержит встроенные функции Perl в исходном виде, и даже замещая одну из них, все равно с помощью CORE можно вызвать исходную версию.

Непоименованные подпрограммы

Perl позволяет создавать безымянные (анонимные) подпрограммы. Например, в следующем случае мы создаем ссылку на подпрограмму (ссылки подробно разбираются в следующей главе), не присваивая подпрограмме имени:

$coderef = sub {print "Hello!\n";};

Обратите внимание, что эту команду требуется завершить точкой с запятой, хотя при обычном определении подпрограмм она не требуется. Хотя у подпрограммы и нет имени, ее можно вызвать с префиксом& (заключив ссылку в фигурные скобки):

&{$coderef};

Hello!

Создание таблиц диспетчеризации подпрограмм

Таблица диспетчеризации подпрограмм (subroutine dispatch table) — это создаваемая пользователем структура данных (массив или хеш), содержащая ссылки на подпрограммы. Можно указать, какую подпрограмму требуется вызвать, указав индекс (в случае массива) или ключ (в случае хеша). Если есть данные, которые нужно обрабатывать несколькими однотипно вызываемыми подпрограммами, то зачастую полезнее оказывается доступ, не по имени, а по индексу или ключу.

Рассмотрим следующий пример. У нас есть две подпрограммы: одна переводит градусы Цельсия в градусы Фаренгейта, другая совершает обратную процедуру:

sub ctof # centigrade to Fahrenheit

{

$value = shift(@_);

return 9 * $value / 5 + 32;

}

sub ftoc # Fahrenheit to centigrade

{

$value = shift(@_);

return 5 * ($value - 32) / 9;

}

Чтобы поместить функции в таблицу диспетчеризации, мы запоминаем ссылки на подпрограммы (ссылки рассматриваются в следующей главе):

$tempconvert[0] = \&ftoc;

$tempconvert[1] = \&ctof;

Теперь можно с помощью индекса выбирать, какую именно подпрограмму вызывать:

print "Zero centigrade is " . &{$tempconvert[1]}(0)

. " Fahrenheit.\n";

Zero centigrade is 32 Fahrenheit.

Подсказка: Чтобы передать параметры вызываемой таким образом подпрограмме, надо поместить список параметров в круглые скобки, следующие за ссылкой на подпрограмму.

[A1]Пример содержал ошибку + добавлено пояснение.

Глава 8. Ссылки в Perl

Коротко

Ссылки — новый фундаментальный тип данных в Perl, впервые введенный для версии 5. Поведение ссылок в значительной мере напоминает поведение указателей в таких языках: как С. Как следует из названия, ссылка ссылается на элемент данных. Чтобы добраться до настоящих данных, надо разыменовать (dereference) ссылку.

С помощью ссылок можно создавать в Perl сложные структуры данных (примеры будут в главе 13). Ссылки используются для потрясающего числа приемов программирования — в частности, для создания анонимных массивов, хешей, подпрограмм и шаблонов функций (все эти темы рассматриваются в данной главе). Если подпрограмме передается более одного массива или хеша, которые при этом надо сохранить как индивидуальные структуры данных, без ссылок не обойтись. (В противном случае элементы всех массивов и хешей сливаются в один длинный список.) При создании объекта и использовании стандартной техники обращения к конструктору класса, сам конструктор, как правило, возвращает ссылку на объект, а не сам объект. Следовательно, ссылки — это еще и фундаментальный инструмент объектно-ориентированного программирования в Perl.

В Perl имеется два типа ссылок: жесткие (hard references) и символические (symbolic references). В начале главы приведен кратким обзор этих двух типов.

Жесткие ссылки

Пусть имеется переменная с именем $variable1:

$variable1 = 5;

Чтобы создать жесткую ссылку на нее, используется оператор «обратная косая черта»:

$variable1 = 5;

$reference = \$variable1;

Теперь переменная $reference содержит ссылку на $variable1 (под ссылкой понимается адрес переменной в памяти и тип переменной). Ссылки такого типа называются жесткими. Скалярные переменные Perl могут хранить жесткие ссылки в качестве значений. Чтобы разыменовать ссылку, используется оператор $ — см. следующий пример, в котором значение, на которое ссылается переменная $reference, получается с помощью разыменования через:

$variable1 = 5;

$reference = \$variable1;

print $$reference;

Разыменование ссылки позволяет получить исходные данные:

$variable1 = 5;

$reference = \$variable1;

print $$reference;

5

Что получится, если надо проверить значение ссылки? В этом случае мы увидим фактический адрес и тип переменной $variable1 в области данных интерпретатора Perl:

$variable1 = 5;

$reference = \$variable1;

print $$reference;

SCALAR(0x8a57d4)

Так выглядит жесткая ссылка в Perl.

Символические ссылки

Символическая ссылка не содержит адрес и тип элемента данных, только его имя. Вернемся к переменной $variable1 из предыдущего примера:

$variable1 = 5;

Ее имя можно присвоить другой переменной $variablename (обратите внимание, при задании имени опускается разыменовывающий префикс $):

$variable1 = 5;

$variablename = "variable1";

Разыменование имени переменной превращает это имя в ссылку на данные, хранящиеся в исходной переменной. Этот процесс и называется созданием символической ссылкой. Вот как этот метод работает с оператором $:

$variable1 = 5;

$variablename = "variable1";

print "$$variablename\n";

5

Вместо оператора $ для разыменования ссылок можно также использовать оператор-стрелку ->.

Оператор-стрелка

Оператор-стрелка — другая популярная форма оператора разыменования ссылок. Он используется со ссылками на массив, хеш, подпрограмму, и сопровожадется индексом, ключом, списком аргументов:

@array = (1, 2, 3);

$arrayreference = \@array;

print $arrayreference->[0];

1

Подробнее об операторе-стрелке рассказывается далее в этой главе.

Анонимные массивы, хеш-таблицы, подпрограммы

В Perl можно создавать массивы, хеши и подпрограммы, для доступа к которым используются ссылки, а не имена. Например, пример из предыдущего раздела сокращается, если сразу создать анонимный массив и присвоить ссылку на него переменной $arrayreference:

$arrayreference = [1, 2, 3];

Чтобы обратиться к содержимому массива, потребуется оператор разыменования:

$arrayreference = [1, 2, 3];

print $$arrayreference[0];

1

С помощью конструкций типа анонимных массивов, создаются сложные структуры данных типа массива массивов. В этой главе демонстрируются подобные приемы (а также многое другое, имеющее отношение к ссылкам). Обратимся к подробностям.

Непосредственные решения

Создание ссылки

Для создания ссылки используется оператор «обратная косая черта» (\). Ссылки, созданные с его помощью, называются жесткими. Кроме того, ссылку можно извлечь прямо из таблицы символов Perl — эта информация содержится в разделе «Как извлечь ссылку из таблицы символов» далее в этой главе. Кроме того, существуют символические ссылки — подробно о них рассказывается в разделе «Создание символических ссылок».

Оператор «обратная косая черта» может использоваться для скалярных переменных, массивов, хешей, подпрограмм или констант. Например, вот так создается жесткая ссылка на значение-константу:

$reference = \"Hello!";

Точно также создаются ссылки для переменных, массивов, хешей, подпрограмм, и т. д. (обратите внимание, что ссылки хранятся в скалярных переменных):

$scalarreference = \$myvariable;

$arrayreference = \@myarray;

$hashreference = \%myhash;

$codereference = \&mysubroutine;

$globreference = \*myname;

Чтобы получить значение переменной через ссылку, используется оператор разыменования:

$reference = \"Hello!";

print $$reference;

Hello!

Можно использовать столько уровней вложенности, сколько вам хочется. Например, в следующем примере создается ссылка на ссылку на ссылку на ссылку:

$reference4 = \\\\"Hello!";

А теперь она разыменовывается с помощью оператора $:

print $$$$$reference;

Hello!

Интересное свойство Perl состоит в том, что ссылки создаются автоматически, в момент разыменования, в предположении, что они существуют:

$$reference = 5;

print "$$reference\n";

5

Как легко заметить, в этом примере ссылка (то есть адрес в памяти) используется до того, как она реально появилась. После выполнения приведенного выше кода, ссылка действительно возникнет — этот процесс называется в Perl самооживлением (autovivification):

print "$reference\n";

SCALAR(0x8a0b14)

В следующем примере ссылки используются для передачи подпрограмме в качестве параметров двух массивов. Подпрограмма поэлементно складывает их (предполагая, что массивы имеют одинаковую длину), возвращая в качестве результата массив. Передача ссылок вместо самих массивов предотвращает объединение их содержимого в один неразличимый список:

@a = (1, 2, 3);

@b = (4, 5, 6);

sub addem

{

my ($reference1, $reference2) = @_;

for ($loop_index = 0;

$loop_index <= $#$reference1;

$loop_index++)

{

$result[$loop_index] = @$reference1[$loop_index]

+ @$reference2[$loop_index];

}

return @result;

}

@array = addem(@a, @b);

print join ’, ’, @array);

5, 7, 9

Ссылки на анонимные массивы

В начале этой главы уже создавался массив без имени, названный нами анонимным. Для этой цели использовался генератор анонимных массивов (anonymous array composer) — пара квадратных скобок:

$arrayreference = [1, 2, 3];

Эта конструкция возвращает ссылку на анонимный массив, которая присваивается скалярной переменной $arrayreference. До элементов массива можно добраться, разыменовав ссылку, но имя массива для этой цели использовать нельзя — его попросту не существует:

$arrayreference = [1, 2, 3];

print $$arrayreference[0];

1

Для разыменовывания ссылки на безымянный массив можно также использовать оператор-стрелку:

$arrayreference = [1, 2, 3];

print $arrayreference->[0];

1

Подсказка: Работа оператора-стрелки подробно описывается далее в разделе «Разыменование ссылок с помощью оператора-стрелки».

Анонимные массивы дают возможность программистам на Perl использовать полезный трюк, состоящий в интерполировании внутрь строки, ограниченной двойными кавычками, результат вызова подпрограммы или результат вычисления. В следующем примере встроенная функция Perl uc переводит в верхний регистр строку:

print "@{[uc(hello)]} there.\n";

HELLO there.

Разберемся, что делает эта конструкция. Perl обрабатывает конструкцию @{} как блок. В процессе его вычисления создается ссылка на анониманый массив из одного-единственного элемента — результата вызова функции uc. При разыменовании ссылки на анонимный массив результат интерполируется внутрь строки.

Ссылки на анонимные хеши

Можно создать ссылку на хеш без имени — он также называется анонимным. Для этой цели используется генератор анонимных хешей (anonymous hash composer) — пара фигурных скобок:

$hashreference = {

Name => Tommie,

ID => 1234,

};

Мы создали анонимный хеш, добавили в него две пары ключ/значение и поместили ссылку на хеш в скалярную переменную $hashreference. Теперь можно использовать ее как обычный хеш, но сначала надо разыменовать ссылку:

print $$hashreference{Name}

Tommie

Для этой цели может служить и оператор-стрелка:

$hashreference = {

Name => Tommie,

ID => 1234,

};

print $hashreference->{Name}

Tommie

Подсказка: Работа оператора-стрелки подробно описывается далее в разделе «Разыменование ссылок с помощью оператора-стрелки».

Ссылки на анонимные подпрограммы

Можно создать ссылку на подпрограмму без имени — такая подпрограмма называется анонимной. Для этой цели используется генератор анонимных подпрограмм (anonymous subroutine composer) — ключевое слово sub:

$codereference = sub {print "Hello!\n"};

Обратите внимание, что в конце команды пришлось добавить точку с запятой, тогда как в случае обычных подпрограмм этого делать не надо. Чтобы вызвать подпрограмму, надо разыменовать ссылку и добавить префикс &:

&$codereference;

Hello!

Также можно передать анонимной подпрограмме параметры:

$codereference = sub {print shift};

&{$codereference}("Hello!\n");

Hello!

При желании для разыменования ссылки на подпрограмму можно использовать оператор-стрелку (подробнее об этом см. раздел «Разыменование ссылок с помощью оператора-стрелки» далее в этой главе):

$codereference = sub {print shift};

$codereference->("Hello!\n");

Hello!

Как извлечь ссылку из таблицы символов

Записи в таблице символов Perl — это хеши, которые содержат адреса всех именованных элементов текущего пакета. Групповому имени name соответствует хеш *name, индексированный ключами SCALAR, HASH, CODE, и т. д. Это значит, что когда вам требуется ссылка на элемент данных, вы можете извлечь ее прямо из таблицы символов, а не использовать оператор \:

$scalarreference = *name{SCALAR};

$arrayreference = *name{ARRAY};

$hashreference = *name{HASH};

$codereference = *name{CODE};

$ioreference = *name{IO};

$globreference = *name{GLOB};

Например, чтобы получить ссылку на переменную с именем $variable1 и с ее помощью вывести значение этой переменной, можно использовать код:

$variable1 = 5;

$scalarreference = *variable1{SCALAR}

print $$scalarreference;

5

Точно так же можно получить ссылку на подпрограмму:

sub printem

{

print "Hello!\n";

}

$codereference = *printem{CODE};

&$codereference;

Hello!

Конструкция *name{IO} возвращает дескриптор потока ввода и/или вывода — то есть, дескриптор файла, сокет, дескриптор каталога.

Подсказка 1: Нельзя получить ссылку на дескриптор потока ввода/вывода с помощью оператора \.

Подсказка 2: Доступ к ссылке через записи в таблице символов зависит от того, использовался ли соответствующий элемент данных (то есть, существует ли соответствующий символ). Если символа, для которого вам нужна ссылка, не существует, вместо ссылки вы получите значение undef. Ранние версии Perl возвращали ссылку на, скажем, анонимный скаляр, если выражение вида *newscalar{SCALAR} запрашивалось до инициализации переменной $newscalar, однако для Perl версии 5 это не так.

Разыменование ссылок

Для разыменовывания ссылки (то есть доступа к данным, на которые она указывает) используется оператор $. В этой главе уже приводились примеры его работы:

$variable1 = 5;

$reference = \$variable1;

print $$reference;

5

Оператор $ используется везде, где есть идентификаторы или их наборы. Вот несколько примеров разыменования базовых типов данных Perl:

sclar = $$scalarreference;

@array = @$arrayreference;

%hash = %$hashreference;

&$codereference($argument1, $argument2);

*glob = *$globreference;

Как уже было показано, с помощью оператора $ можно также одновременно разыменовывать несколько уровней ссылок:

$reference4 = \\\\"Hello!";

print $$$$$reference;

Hello!

Кроме простых случаев, можно добавлять индекс, когда разыменованию подвергается ссылка на массив:

@array = (1, 2, 3);

$arrayreference = \@array;

print $$arrayreference[0];

1

Точно так же в случае разыменования ссылки на хеш-таблицу можно указывать ключ:

%hash = (

Name => Tommie,

ID => 1234,

);

$hashreference = \%hash;

print $$hashreference{Name};

Tommie

При разыменовывании ссылки на подпрограмму можно указать список аргументов:

sub printem

{

print shift;

}

$codereference = \&printem;

$codereference->("Hello!\n");

Hello!

Прямая ссылка иногда заменяется возвращающим ее блоком. Вот как приведенные выше примеры выглядят в случае применения блоков:

$sclar = ${$scalarreference};

@array = @{$arrayreference};

%hash = %{$hashreference};

&{$codereference}($argument1, $argument2);

*glob = *{$globreference};

И последнее. Записи таблицы символов (тип данных typeglob) могут разыменовываться точно так же, как и ссылки, поскольку они содержат ссылки на все типы данных, ассоциированные с этим именем. При разыменовании ссылки с помощью префикса всегда надо указывать, какого типа данное вы расчитываете получить на выходе. Этот же самый префикс помогает Perl понять, какой из элементов данных, ассоциированных с заданным именем, вы имеете в виду при разыменовывании типа данных typeglob. Например, в следующем коде требуется получить скалярную переменную (разыменовывающий префикс $):

$variable1 =5;

print ${variable1};

5

То же самое мы получаем, если требуется массив (разыменовывающий префикс @):

@array = (1, 2, 3);

print join(", ", @{*array});

1, 2, 3, 4

Разыменование ссылок с помощью оператора-стрелки

При работе с массивами, хешами и подпрограммами для простого разыменования ссылок можно использовать оператор-стрелку. Например, вот так оператор-стрелка работает в сочетании с ссылкой на массив:

$arrayreference = [1, 2, 3];

print $arrayreference->[0];

1

Можно также создавать массивы массивов, как в следующем примере. Здесь создается анонимный массив, состоящий из двух других анонимных массивов:

$arrayreference = [[1, 2, 3], [4, 5, 6]];

Чтобы ссылаться на элементы массива массивов, используется следующий синтаксис:

$arrayreference = [[1, 2, 3], [4, 5, 6]];

print $arrayreference->[1][1];

5

(Обратите внимание на отсутствие оператора-стрелки между двумя парами квадратных скобок. Подробнее этот вопрос рассматривается далее в разделе «Когда можно опускать оператор-стрелку».)

Подсказка: Более подробно о массивах массивов рассказывается в главе 13.

Вы можете также использовать оператор-стрелку при работе со ссылками на хеш-таблицы:

$hashreference->{key} = "This is the text.";

print $hashreference->{key};

This is the text.

(В этом примере для создания элемента, на который ссылается переменная $hashreference, мы полагаемся на процесс самооживления (autovivification), о котором уже рассказывалось выше в разделе «Создание ссылки».)

А вот как используется оператор-стрелка при ссылке на подпрограмму:

sub printem

{

print shift;

}

$codereference = \&printem;

$codereference->("Hello!\n");

Hello!

В общем случае слева от оператора-стрелки может стоять любое выражение, возвращающее ссылку:

$dataset[$today]->{prices}->[1234] = "\$4999.99";

Когда можно опускать оператор-стрелку

Оператор-стрелку в Perl необязательно ставить между квадратными и круглыми скобками. Поэтому пример из предыдущего раздела:

$dataset[$today]->{prices}->[1234] = "\$4999.99";

можно записать как:

$dataset[$today]{prices}[1234] = "\$4999.99";

Perl разрешает опускать оператор-стрелку между скобками прежде всего затем, чтобы позволить работать с массивами массивов и сделать их похожими на многомерные массивы других языков программирования. Приведем пример:

@array = ([1, 2], [3, 4]);

print $array[1][1];

4

Как определить тип ссылки с помощью оператора ref

Оператор ref помогает определить, на элемент какого типа ссылается ссылка. Синтаксис вызова ref выглядит так:

ref выражение

ref

Этот оператор возвращает значение истина (ненулевое значение), если выражение — это ссылка, и ложь в противном случае. Если не указано выражение, то оператор ref использует специальную переменную $_. Значение, возвращаемое как истина, отражает тип элемента, на который ссылается ссылка. Встроенные типы Perl соответствуют значениям:

REF SCALAR ARRAY HASH CODE GLOB

Вот пример, в котором оператор ref применяется к ссылке на скаляр:

$variable1 = 5;

$scalarref = \$variable1;

print (ref $scalarref);

SCALAR

Создание символических ссылок

Жесткая ссылка содержит адрес элемента в памяти и его тип. Символическая ссылка вместо адреса содержит имя элемента. Тем самым последняя заставляет Perl искать переменную с заданным именем вместо того, чтобы напрямую обратиться по нужному адресу.

В следующем примере создается символическая ссылка на переменную $variable1 и затем эта ссылку используется для доступа к исходной переменной:

$variable1 = 0;

$variablename = "variable1";

$$variablename = 5;

print "$variable1\n";

5

(Обратите внимание, что символическая ссылка содержит имя переменной без разыменовывающего префикса $.)

Как и в случае жестких ссылок, использование символических ссылок с еще не существующими элементами данных приводит к созданию этих элементов. В следующем примере переменная $variable1 появилась только после того, как на нее сослались:

$variablename = "variable1";

${$variablename} = 5;

print "$variable1\n";

5

Точно так же можно создавать символические ссылки на элементы данных типа массивов или хеш-таблиц:

$arrayname = "array1";

$arrayname->[1] = 5;

print "$array1[1]\n";

5

С помощью символических ссылок можно ссылаться даже на подпрограммы:

$subroutinename ="subroutine1";

sub subroutine1

{

print "Hello!\n";

}

&$subroutinename();

Hello!

С помощью символических ссылок можно ссылаться на глобальные и локальные переменные текущего пакета. Переменные с лексической областью видимости (то есть, описанные с ключевым словом my) не содержатся в таблице символов, и поэтому на них нельзя ссылаться с помощью символических ссылок. Например, в следующем фрагменте кода вместо значения переменной, на которую указывает символическая ссылка, выводится пустая строка (Perl не может найти переменную и интерпретирует ее значение как undef):

my $variable1 = 10;

$variablename = "variable1"; # Будет проблема

print "The value is $$variablename\n";

# Приведенный выше код выдаст неполный результат:

The value is

Запрет символических ссылок

Очень сложно использовать жесткие ссылки невольно, но вполне возможно использовать символическую ссылку (то есть, имя переменной, на которую ссылаются) в результате ошибки. Чтобы запретить использование символических ссылок, следует включить в сценарий прагму:

use strict ’refs’

До окончания блока с такой прагмой Perl разрешает использовать только жесткие ссылки. Однако, если вы хотите разрешить символические ссылки внутри вложенного блока, задайте другую прагму:

no strict ’refs’

Использование ссылок на массивы как ссылок на хеши

Начиная с версии Perl 5.005 там, где должна находиться ссылка на хеш, разрешается использовать ссылку на массивы (по крайней мере, это можно делать в большинстве случаев). Иными словами, при определенной организации массива к его элементам можно обращаться по именам.

Подсказка: Это новое и экспериментальное свойство Perl. Оно может измениться в будущем.

Чтобы использовать ссылку на массив в качестве ссылки на хеш, необходимо добавить в массив дополнительную информацию: установить соответствие между ключами хеша и индексами массива. Эта информация заносится в нулевой элемент массива в следующем формате:

{key1 => index1, key2 => index2, key3 => index3, ...}

В следующем примере создается ссылка на анонимый массив, к которому добавлена информация о хеш-ключах с именами first и second:

$arrayref = [{first=>1, second=>2}, "Hello", "there"];

Теперь на элементы массива можно ссылаться с помощью ключей, что иллюстрирует следующий пример:

$arrayref = [{first=>1, second=>2}, "Hello", "there"];

print $arrayref->{first} . " " . $arrayref->{second};

Hello there

Как создать замыкание области видимости в устойчивую форму

Замыкание (closure) — анонимная подпрограмма, имеющая доступ к переменным, лексическая область видимости которых входила в область видимости подпрограммы в момент ее компиляции. Такая подпрограмма сохраняет нужные переменные в своей области видимости (а именно, не дает уничтожить их при автоматической сборке мусора), даже если вызывается позднее. Замыкания позволяют передавать подпрограмме данные в момент ее определения так, чтобы надлежащим образом инициализировать внутренние переменные.

Рассмотрим пример, который поможет понять, о чем идет речь. В следующем фрагменте кода создается подпрограмма printem, которая возвращает ссылку на анонимную подпрограмму. При вызове последняя получает доступ к строке, исходно находившейся внутри printem, даже если эта строка давно вышла из области видимости. Тем самым анонимная подпрограмма печатает как текст, переданный ей в качестве параметра, так и текст, изначально передававшийся printem:

sub printem

{

my $string1 = shift;

return sub {my $string2 = shift;

print "$string1 $string2\n";};

}

Теперь в строку $string1 подпрограммы printem заносится текст «Hello», а ссылка на анонимную подпрограмму записывается в переменную $hello:

$hello = printem("Hello");

В результате при вызове анонимной подпрограммы с некоей строкой в качестве аргумента, та сохраняет доступ также и к исходной строке и может вывести обе:

&$hello("today.");

&$hello("there.");

Hello today.

Hello there.

Таким способом подпрограмма инициализиуется до начала работы с ней. Однако подобные замыкания возможны только для переменных с лексической областью видимости. В следующем разделе приводится дополнительная информация о замыканиях.

Создание функций на основе шаблонов

Замыкания можно использовать для создания шаблонов функций, позволяющих создавать и настраивать пользовательские функции в процессе работы сценария.

Рассмотрим пример. Мы будем использовать шаблон для создания трех новых функций — printHello, printHi и printGreetings. Они будут выводить, соответственно, строки «Hello», «hi» и «Greetings». Начнем с того, что запомним эти строки в массиве:

@gretings = ("Hello", "Hi", "Greetings");

Далее, мы выполним цикл foreach по элементам массива с использованием лексической переменной в качестве индекса (чтобы использовать замыкания, необходимы лексические переменные — см. предыдущий раздел). На каждой итерации цикла создается непоименованная функция, использующая очередной элемент массива @greetings, и запись таблицы символов (тип данных typeglob) для этой функции:

foreach my $term (@greetings) {

*{"print" . $term} = sub {print "$term\n"};

}

Теперь можно вызывать созданные на основе шаблона пользовательские функции printHello, printHi и printGreetings:

printHello();

printHi();

printGreetings();

Hello

Hi

Greetings

Так и работают шаблоны функций. Если бы мы просто запомнили ссылки на анонимные подпрограммы в подходящих скалярных переменных:

foreach my $term (@greetings) {

${"print" . $term} = sub {print "$term\n"};

}

то могли вызывать эти подпрограммы через разыменовывания ссылок, а не как «истинные» подпрограммы:

&$printHello();

&$printHi();

&$printGreetings();

Hello

Hi

Greetings

Глава 9. Встроенные переменные

Коротко

Perl работает со множеством встроенных переменных. О некоторых из них речь уже шла — например, о безусловном фаворите среди специальных переменных Perl — переменной $_, используемой по умолчанию многими функциями:

while ($_ = <>) {

print $_;

}

Поскольку $_ используется по умолчанию и для чтения потока ввода, и для вывода, приведенный выше код совпадает с

while (<>) {print;}

Perl содержит много встроенных переменных, и в этой главе будет рассказано о всех.

Подсказка: В этой главе много материала, специфичного для Unix, поскольку многие встроенные переменные были введены для учета индивидуальных особенностей именно этой операционной системы.

Некоторые встроенные переменные предназначены только для чтения (такие случаи оговариваются при описании переменной специально). При попытке присвоения такой переменной нового значения, Perl выдаст сообщение об ошибке. С другой стороны, использовать некоторые переменные и вовсе не рекомендуется (а именно, хотя они и доступны для программиста, обращение к ним может вызвать проблемы) — на такие случаи также обращаетсся особое внимание.

Подсказка: Эта глава является кратким справочником по всем встроенным переменным Perl. Как результат, некоторые переменные ссылаются на свойства, которые описываются в этой книге дальше. Более того, некоторые переменные ссылаются на свойства, которые в этой книге либо вообще не обсуждаются, либо об их существовании только упоминаетя — в таких случаях за дополнительной информацией придется обратиться к документации, сопровождающей Perl. (Это означает, что программисту в повседневной практике нет необходимости использовать соответствующие свойства Perl.)

Итак, на этом введение завершается. После знакомства со справочной информацией из двух последующих разделов, можно переходить к основной части главы, описывающей встроенные переменные Perl.

Развернутые имена встроенных переменных

По большей части встроенные переменные Perl названы очень кратко и загадочно — например $] или $<. Однако, как правило, для них имеются эквивалентные развернутые имена, напоминающие в мнемонической (для владеющих английским языком) форме, что именно делает та или иная переменная. Развернутые имена встроенных переменных можно использовать, если поместить в начале сценария специальную прагму:

use English;

Она позволяет использовать для встроенных переменных, перечисленных в таблице 9.1, английские эквиваленты. Учтите, что некоторые встроенные переменные имеют несколько мнемонических эквивалентов.

Таблица 9.1. Развернутые эквиваленты имен встроенных переменных

Переменная

Развернутая форма

$’

$POSTMATCH

$-

$FORMAT_LINES_LEFT

$!

$OS_ERROR

$ERRNO

$"

$LIST_SEPARATOR

$#

$OFMT

$$

$PROCESS_ID

$PID

$%

$FORMAT_PAGE_NUMBER

$&

$MATCH

$(

$REAL_GROUP_ID

$GID

$)

$EFFECTIVE_GROUP_ID

$EGID

$*

$MULTIPLE_MATCHING

$,

$OUTPUT_FIELD_SEPARATOR $OFS

$.

$INPUT_LINE_NUMBER $NR

$/

$INPUT_RECORD_SEPARATOR $RS

$:

$FORMAT_LINE_BREAK_CHARACTERS

$;

$SUBSCRIPT_SEPARATOR $SUBSEP

$?

$CHILD_ERROR

$@

$EVAL_ERROR

$\

$OUTPUT_RECORD_SEPARATOR $ORS

$]

$PERL_VERSION

$^

$FORMAT_TOP_NAME

$^A

$ACCUMULATOR

$^D

$DEBUGGING

$^E

$EXTENDED_OS_ERROR

$^F

$SYSTEM_FD_MAX

$^I

$INPLACE_EDIT

$^L

$FORMAT_FORMFEED

$^O

$OSNAME

$^P

$PERLDB

$^T

$BASETIME

$^W

$WARNING

$^X

$EXECUTABLE_NAME

$_

$ARG

$`

$PREMATCH

$|

$OUTPUT_AUTOFLUSH

$~

$FORMAT_NAME

$+

$LAST_PAREN_MATCH

$<

$REAL_USER_ID

$UID

$=

$FORMAT_LINES_PER_PAGE

$>

$EFFECTIVE_USER_ID $EUID

$O

$PROGRAM_NAME

$ARGV

нет синонима

$nn

нет синонима

%ENV

нет синонима

%INC

нет синонима

%SIG

нет синонима

@_

нет синонима

@ARGV

нет синонима

@INC

нет синонима

Настройка встроенных переменных на конкретные дескрипторы файлов

Многие встроенные переменные работают с текущим дескриптором файла (более подробно об этом рассказывается в главе 12). Однако, при наличии прагмы

use FileHandle;

в начале сценария, то переменные можно настраивать на работу с конкретным дескриптором файла. Для этой цели можно применить методы, которые выполняются с помощью конструкций вида

метод дескриптор выражение

или, используя немного другой синтаксис, как

дескриптор->метод(выражение);

Методы приводятся в таблице 9.2.

Таблица 9.2. Методы, настраивающие встроенные переменные на работу с конкретным дескриптором файла

Переменная

Вызов метода

$-

format_lines_left HANDLE EXPR

$%

format_page_number HANDLE EXPR

$,

output_field_separator HANDLE EXPR

$.

Input_line_number HANDLE EXPR

$/

input_record_separator HANDLE EXPR

$:

format_line_break_characters HANDLE EXPR

$\

output_record_separator HANDLE EXPR

$^

format_top_name HANDLE EXPR

$^L

format_formfeed HANDLE EXPR

$|

autoflush HANDLE EXPR

$~

format_name HANDLE EXPR

$=

format_liner_per_page HANDLE EXPR

Непосредственные решения

$’ — строка, следующая за совпадением

Синоним: $POSTMATCH

Переменная $’ содержит фрагмент заданного пользователем текста, который следует за фрагментом, сопоставленным шаблону в процессе последней операции поиска или замены. Пример:

$text = "earlynowlate";

$text =~ /now/;

print "Prematch: \"$`\" " .

"Match: \"$&\" " .

"Postmatch: \"$’\"\n";

Prematch: "early" Match: "now" Postmatch: "late"

Эта переменная доступна только для чтения.

$- — число строк, оставшихся на странице

Синоним: $FORMAT_LINES_LEFT

Переменная $- содержит число строк, которые остались на странице текущего потока вывода.

$! — текущая ошибка

Синонимы: $OS_ERROR, $ERRNO

Переменная $! выводит номер текущей ошибки в числовом контексте, и текстовую строку — сообщение об ошибке — при работе в строковом контексте. Пример:

use File::Copy;

# Попытка скопировать несуществующий файл

copy ("nonexistent.pl", "new.pl");

print $!;

No such file or directory

$"— разделитель полей массивов при интерполировании

Синоним: $LIST_SEPARATOR

Эта переменная идентична переменнлй $, (она рассматривается далее) за тем исключением, что относится не к массивам, выводимым по команде print, а к массивам, интерполируемым внутрь строк. А именно, $ " содержит символ, используемый как разделитель полей массивов, когда Perl интерполирует содержимое массива в строку, заключенную в двойные кавычки. По умолчанию используется пробел. Пример:

@array = (1, 2, 3);

$" = ’,’;

$text = "@array";

print $text;

1,2,3

$#— формат вывода чисел с плавающей точкой

Синоним: $OFMT

Переменная $# задает формат по умолчанию для вывода чисел с плавающей точкой с помощью команды print. Пример:

$pi = 3.14159265359;

$# = ’%.6g’;

print "$pi\n";

3.14159

Предупреждение: Использование переменной $# не рекомендуется.

$$— идентификатор процесса Perl

Синонимы: $PROCESS_ID, $PID

Переменная $$ содержит идентиификатор процесса интерпретатора Perl, выполняющего текущий сценарий.

$%— текущая страница вывода

Синоним: $FORMAT_PAGE_NUMBER

Переменная $% содержит номер текущей страницы для текущего потока вывода.

$&— совпадение с шаблоном поиска

Синоним: $MATCH

Переменная $& содержит фрагмент текста, сопоставленный шаблону при последней операции поиска или замены. Пример:

$text = "earlynowlate";

$text =~ /now/;

print "Prematch: \"$`\" " .

"Match: \"$&\" " .

"Postmatch: \"$’\"\n";

Prematch: "early" Match: "now" Postmatch: "late"

Эта переменная предназначена только для чтения.

$(— реальный идентификатор группы пользователей (real GID)

Синонимы: $REAL_GROUP_ID, $GID

Переменная $( содержит реальный идентификатор группы пользователей (real GID) для текущего процесса. Эта информация полезна только в среде Unix. Если операционная система поддерживает членство в нескольких группах одновременно, переменная $( содержит список групп, в которые входит пользователь, от имени которого запущен сценарий.

$)— текущий идентификатор группы пользователей (effective GID)

Синонимы: $EFFECTIVE_GROUP_ID, $EGID

Переменная $) содержит текущий идентификатор группы пользователей (effective GID) для текущего процесса. Эта информация полезна только в среде Unix. Если операционная система поддерживает членство в нескольких группах одновременно, переменная $( содержит список групп, в которые входит пользователь, от имени которого запущен сценарий.

Подсказка: В чем отличие текущей группы от реальной? Программа может быть запущена любым пользователем из другой группы, но права доступа она получает от группы владельца файла, то есть текущей группы.

$*— совпадение с шаблоном поиска

Синоним: $MULTIPLE_MATCHING

Переменная $* позволяет выполнять поиск в многострочных строках — то есть строках, содержащих символы новой строки \n. Если переменная $* установлена в 1, то метасимволы шаблона поиска ^ и $ сопоставляются позициям перед и после внутренних символов новой строки, если в 0 (значение по умолчанию) — только физическому началу и концу исследуемого текста. Пример:

$text = "Here \nis the\ntext.";

$text =~ /^is/;

print $&; # Совпадение не найдено

$* = 1;

$text =~ /^text/;

print $&; # Совпадение найдено

text

Предупреждение: Работа с переменной $* в Perl не приветствуется. Для этой цели обычно используются модификаторы m и s.

$,— разделитель полей вывода

Синонимы: $OUTPUT_FIELD_SEPARATOR, $OFS

Переменная $, содержит разделитель полей для оператора print. Следующий пример показывает, как использовать эту переменную:

$, = ’;’;

print 1, 2, 3;

1;2;3

$.— текущий номер строки ввода

Синонимы: $INPUT_LINE_NUMBER, $NR

Переменная $. содержит текущий номер строки ввода для последнего открытого дескриптора файла.

$/— разделитель входных записей

Синонимы: $INPUT_RECORD_SEPARATOR, $RS

Переменная $/ содержит разделитель входных записей. При чтении данных через дескриптор файла содержимое переменной $/ выступает в качестве ограничителя очередной записи. По умолчанию $/ содержит символ новой строки \n.

Рассмотрим следующий интересный пример. Обычно содержимое файла считывается построчно. Однако, если сделать переменную $/ неопределенной, то можно прочесть весь многострочный файл за один раз:

undef $/;

open HANDLE, "file.txt";

$text = <HANDLE>;

print $text;

Here’s

text from

a file.

$:— маркер разбивки строки

Синоним: $FORMAT_LINE_BREAK_CHARACTERS

Переменная $: содержит текст — маркер разбивки строки при выводе. После вывода содержимого $: Perl имеет право разбивать строку, чтобы заполнить поля формата.

$;— разделитель индексов

Синонимы: $SUBSCRIPT_SEPARATOR, $SUBSEP

Переменная $; позволяет эмулировать многомерные массивы с помощью хеш. Эта переменная содержит текст, выступающий в роли разделителя индексов, при передаче хешу индексов, заданных в стиле многомерных массивов. Совокупный ключ, получающийся при объединении индексов и разделителей в единую строку, служит ключом доступа к элементу. По умолчанию эта переменная содержит запятую. Ниже приводятся два эквивалентных выражения:

$hash{x,y,z}

$hash{join($;, x, y, z)}

Вот еще один пример использования хеша в роли массива — здесь в качестве индекса задан ключ 1,1,1:

$hash{"1$;1$;1"} = "Hello!";

print $hash{1,1,1};

Hello!

Однако вместо хеша предпочтительнее работать с «истинными» многомерными массивами (см. главу 14) — они быстрее для доступа и эффективнее в использовании памяти.

$?— статус последней системной операции

Синоним: $CHILD_ERROR

Переменная $? содержит статус (возможно, номер системной ошибки), возвращенный поледним закрытым каналом, вызовом внешней команды в строке, ограниченной обратными апострофами, или обращением к встроенныи системным операторам Perl.

$@— ошибка выполнения функции eval

Синоним: $EVAL_ERROR

Переменная $@ содержит сообщение об ошибке, которая возникла (если возникла) в процессе последнего обращения к функции eval. Если ошибок не было, эта переменная пуста.

$[— базовый индекс массивов

Синоним: нет.

Переменная $[ содержит число, присваиваемое первому индексу массива. По умолчанию она равна нулю, но вы можете присвоить ей другое значение.

Предупреждение: Использование переменной $[ не приветствуется в Perl.

$\— разделитель выходных записей

Синонимы: $OUTPUT_RECORD_SEPARATOR, $ORS

Переменная $\ содержит символ или строку символов, которые оператор print использует как разделитель выходных записей. Обычно это пустая строка, но ее можно переопределить:

$\ = "END_OF_OUTPUT";

print "Hello!";

Hello!END_OF_OUTPUT

$]— версия Perl

Синоним: $PERL_VERSION

Переменная $] содержит версию интерпретатора Perl, под которым выполняется сценарий. Пример:

print $];

5.00502

$^— текущий формат колонтитула страницы

Синоним: $FORMAT_TOP_NAME

Переменная $^ содержит текущий формат колонтитула страницы для текущего потока вывода.

$^A— накопитель команды write

Синоним: $ACCUMULATOR

Переменная $^A представляет собой накопитель для команды write. После обращения к ее формату, команда write выведет содержимое накопителя.

$^D— текущие флаги отладки

Синоним: $DEBUGGING

Переменная $^D содержит значение текущих флагов, управляющих отладкой сценария Perl.

$^E— информация об ошибке, специфичная для операционной системы

Синоним: $EXTENDED_OS_ERROR

Переменная $^E содержит дополнительную информацию об ошибке, возвращаемую в случае работы под управлением конкретной операционной системы. В большинстве операционных систем, за исключением VMS, OS/2, Win32 и интерпретатора MacPerl для компьютеров Macintosh, переменные $^E и $! содержат один и тот же текст. Вот пример, показывающий особенности операционной среды Windows:

use File::Copy;

# Попытка скопировать несуществующий файл

copy ("nonexistent.pl", "new.pl");

print $!;

print $^E;

No such file or directory

The system cannot find the file specified

$^F— максимальное количество дескрипторов файлов

Синоним: $SYSTEM_FD_MAX

Переменная $^F содержит максимальное количество дескрипторов файлов для операционной системы Unix (обычно 2).

$^H— флаги проверки синтаксиса

Синоним: нет.

Переменная $^H содержит текущий набор правил проверок синтаксиса Perl, заданных с помощью команды use strict и других прагм.

$^I— расширение файлов для редактирования «по месту»

Синоним: $INPLACE_EDIT

Переменная $^I содержит расширение файлов, для которых разрешено редактирование «на месте» (см описание ключа -i в главе 1). Если такой файл открыт как поток ввода, в него можно выводить данные, но в целях безопасности в момент открытия файла Perl создает его резервную копию. Чтобы запретить редактирование «на месте», надо сделать переменную $^I неопределенной (команда undef).

$^L— символ прогона страницы

Синоним: $FORMAT_FORMFEED

Переменная $^L содержит символ, который Perl использует при выводе формата для обоззначения конца страницы. Значение по умолчанию — символ \f.

$^M— буфер памяти «на крайний случай»

Синоним: нет.

Переменная $^M резервирует память, которая может использоваться Perl, когда его внутренние ресурсы исчерпаны. Вообще говоря, Perl не обрабатывает ситуацию недостатка памяти. Однако, есл при компиляции интерпретатора был задан ключ DPERL_EMERGEMCY_SBRK то можно зарезервировать на этот случай дополнительный буфер памяти (скажем, в 1Мб), выполнив команду:

$^M = ’ ‘ [ (2 ** 20);

$^O— имя операционной системы

Синоним: $OSNAME

Переменная $^O содержит имя операционной системы, для которой предназначен данный интерпретатор Perl. В случае операционной системы Unix, для которой интерпретатор обычно собирается самостоятельно, эта переменная помимо имени операционной системы может содержать локальное имя компьютера (не имеющее, как легко понять, отношения к операционной системе). Версии Perl, специально собранные для той или иной операционной системы, как правило, более надежны<$FВообще говоря, надежность обеспечивается компилятором, а не условиями сборки. Если Вы считаете себя профессионалом, то «ручная» сборка Perl может быть для Вас более предпочтительным вариантом — при этом, кроме всего прочего, можно задействовать дополнительные возможности. — Примеч. ред.>:

print $^O;

MSWin32

$^P— поддержка отладки

Синоним: $PERLDB

Переменная $^P содержит флаги, описывающие внутреннюю конфигурацию режима отладки. Вот описание значений некоторых битов: