Библиотека Интернет Индустрии I2R.ru |
|||
|
Perl - это не только CGIКогда упоминается язык Perl, то в первую очередь обычно вспоминают о web-серверах и обслуживании CGI-запросов. При этом как-то в тени остается тот факт, что Perl - это самостоятельный и мощный язык программирования, ориентированный на обработку текстов. При этом он совсем несложен для освоения и может оказать неоценимую помощь web-мастеру в ходе разработки и обслуживания web-сайтов. Ведь всем хорошо известно, насколько трудоемкими оказываются порой операции регулярного обновления, особенно для ежедневных проектов. Все эти тезисы наглядно иллюстрирует предлагаемая вашему вниманию статья - в ней есть все необходимое, чтобы начать писать на Perl небольшие полезные программы. С 1995 года на сервере NASA (antwrp.gsfc.nasa.gov/apod/astropix.html) действует проект Astronomy Picture of the Day (APOD, "Астрономическая картинка дня"). Это, пожалуй, самая знаменитая страница в Интернете, посвященная астрономии. Каждый день множество изображений, присланных профессионалами и любителями, проходят отбор. Самое красивое и интересное из них выставляется на сайт с комментарием, написанным профессиональным астрономом. На сегодня там находится самая большая в мире коллекция аннотированных астрономических картинок. В начале этого лета Юношеская астрономическая школа (ЮАШ, yaseu.da.ru) получила разрешение поддерживать русскоязычное "зеркало" этого проекта apod.da.ru. Поскольку проект ежедневный, потребовалось много времени на его сопровождение. Кроме получаса, ежедневно затрачиваемого на перевод, еще полчаса уходило на рутинные изменения дат и ссылок в пяти различных файлах: сегодняшнем и вчерашнем файлах с переводом, сводном списке страниц и еще в двух вспомогательных файлах. Например, в сегодняшнем файле с переводом нужно в нескольких местах и в разных форматах прописать текущую дату, изменить фамилию и адрес переводчика, заголовки, ключевые слова, ссылки на картинки, на исходную станицу сайта NASA, а также на вчерашний и на завтрашний файлы. Вся эта рутинная работа не только отнимала массу времени, но к тому же требовала большой внимательности - из-за ручного обслуживания нередко случались досадные ошибки. Назрела насущная необходимость в том, чтобы все это делалось автоматически. Кроме экономии времени, гарантировалась бы еще и безошибочность исправлений. Первая идея была написать соответствующую программу на C++, с которым к этому моменту я работала уже несколько лет. По предварительным оценкам, на это потребовалась бы два-три вечера (ну, а в реальности, понятное дело, побольше). Но поскольку у меня были время и энтузиазм, я решила освоить на этой задаче более подходящий инструмент - Perl. Многие думают, что Perl нужен только для написания CGI-приложений на Unix-серверах, однако это не так. Поскольку это язык для обработки текстов, его можно использовать, например, для сопровождения web-сайтов. Кроме того, Perl давно вышел из среды Unix и сегодня является одним из самых переносимых языков. Достать и установить версию, например, под Windows 95/98 не составляет труда. С домашней страницы Perl (www.perl.com) можно взять бесплатные пакеты под очень широкий набор операционных систем. Я скачала ActivePerl 5.6.0. Это одна из самых лучших и наиболее полных версий Perl, портированная под Windows 95/98/2000/NT, Linux RedHat 6.0 и Debian 2.1, а также Solaris 2.6. Дистрибутив ActivePerl 5.6.0 занимает 10 Мбайт, а при установке требует 29 Мбайт. Из них 12 Мбайт составляет подробная документация в формате HTML. При желании за счет документации и ненужных пакетов занимаемое место можно сильно сократить. Специального редактора в поставке нет, но в документации приведены описания разных редакторов и IDE вместе с адресами, где их можно взять. В системе Windows 98, которой я пользуюсь, никаких проблем при инсталляции ActivePerl не возникло. При установке в Windows ActivePerl модифицирует реестр так, что файл с расширением .pl ассоциируется с интерпретатором Perl, и для выполнения скрипта достаточно дважды по нему щелкнуть. Таким образом, Perl-сценарии внешне почти ничем не будут отличаться от других исполняемых файлов. Для проверки, прошла ли установка успешно, можно запустить файл example.pl, который есть в поставке:
Как видите, всего одна строка. В Windows, правда, существует маленькая неприятность: консоль, которую открывает Perl в ходе выполнения программы, после ее завершения сразу закрывается, и с этим ничего нельзя поделать. Поэтому для того, чтобы успеть прочитать приветствие, в конце файла example.pl нужно добавить оператор <> - считать строку. Итак, проведя установку, я стала читать книжку по Perl. Изучать ее от корки до корки совсем не хотелось. Я полистала ее часика два, вникая в те детали, которые казались необходимыми для моей задачи, и села за программу. Далее книжка читалась по мере решения очередных проблем, а решались они довольно быстро. (Я использовала "Perl: Специальный справочник" Стивена Холзнера, ИД "Питер".) На первых этапах - таких как открытие, запись и чтение из файла, - никаких проблем не возникло, что весьма порадовало. Вот пример записи в файл:
OUT - это дескриптор файла, объявлять заранее его не надо. Префикс > означает, что файл нужно открыть для записи. Если файл не открывается, то функция die выводит сообщение об ошибке в выходной поток STDERR и завершает работу программы. Синтаксис Perl во многом похож на С, и это заметно облегчило освоение: не нужно было смотреть описание каждой конструкции, многое было понятно интуитивно или писалось по привычке и срабатывало. Например, фигурные скобки для ограничения блока, операции сравнения, операторы цикла, условные операторы, индексация массива с нуля и т. д. Так почему же стоит использовать Perl, а не С? Рассмотрим это на примере моей задачи. Программа была устроена следующим образом: для того чтобы сформировать сегодняшний файл с переводом, в котором надо сделать больше всего изменений, в качестве исходных данных через командную строку передается вчерашний файл. Из вчерашнего файла последовательно считываются строки, в которые при необходимости вносятся изменения, затем строки записываются в новый файл. После этого сформированный файл переименовывается, причем его имя создается на основе даты, считанной из исходного файла. Таким образом, первая задача была - находить строки по некоторым признакам и заменять в них отдельные фрагменты. Для этих целей в Perl есть очень мощный инструмент - регулярные выражения. С их помощью можно задавать всевозможные шаблоны для поиска и изменять найденный текст. Рассмотрим вкратце их работу. Имеются три операции: m/.../, s/.../.../ и tr/.../.../ (у tr/.../.../ есть другое имя - y/.../.../), которые можно применить к любой строке с помощью операции связывания =~. m/.../ ищет подстроку, совпадающую с шаблоном, который задается регулярным выражением, стоящим на месте многоточия. Например, операция m/.+<\/a>/ возвращает значение "истина", если в строке, с которой она работает, есть подстрока, заключенная в теги и . В этом примере .+<\/a> - регулярное выражение. Точка в регулярном выражении означает любой символ, кроме символа новой строки. Плюс означает один или более таких символов. \/ - это символ '/'. Его приходится задавать таким образом, чтобы он не воспринимался как закрывающий ограничитель m/.../. Операция s/.../.../ ищет подстроку, совпадающую с шаблоном, который стоит на месте первого многоточия, и заменяет строкой, стоящей вместо второго многоточия. tr/.../.../ регулярных выражений не использует, а выполняет замену посимвольно. Вместо первого многоточия стоит список символов, подлежащих замене, а вместо второго - то, на что их заменять. Например, оператор tr/abc/ABC/ заменяет 'a' на 'A', 'b' на 'B', 'c' на 'C'. В следующем примере на словах quasar и quasars ставится ссылка:
Регулярное выражение (quasar\S+|quasar) - это альтернативный шаблон. Выражение \S+ означает, что после букв quasar могут быть один или более любых символов кроме пробела, табуляции и символа новой строки. Символ | является разделителем между альтернативами. Таким образом, мы ищем вхождение в строку либо quasar\S+, либо quasar, причем сначала первое, а потом второе. Поменяй мы их местами, мы получили бы такой результат:
Часть строки, сопоставленная с выражением, заключенным в скобки, помещается в специальную переменную $1, значение которой мы потом вставляем между тегами. Символ g - это спецификатор работы s/.../.../. Он означает, что надо вести глобальный поиск (по умолчанию поиск прекращается после первого совпадения). На самом деле, существует великое множество способов задания шаблонов и спецификаций работы операций. Регулярным выражениям посвящены целые книги, подробно рассматривающие специальные приемы, трюки и готовые решения. Это действительно очень мощное средство. Вернемся к нашей программе. В начале тех строк, где нужно было вносить изменения, я расставила свои теги . Вот строка, в которой нужно сменить вчерашнюю дату на сегодняшнюю:
А вот фрагмент программы, который это выполняет:
На первый взгляд, для человека, незнакомого с языком, это выглядит, наверное, устрашающе. Да, язык птичий, но, тем не менее, посмотрим, что происходит. В первой строке в качестве условия стоит операция m/.../. Для краткости букву m можно опускать, но зато, если она написана, то в качестве ограничителей можно использовать другие символы. Это бывает нужно в тех случаях, когда '/' используется в регулярном выражении. Например, m|.+|. Операцию связывания тоже можно опустить. По умолчанию m/.../, s/.../.../ и tr/.../.../ работают со специальной переменной Perl c именем $_, в которой хранится результат предыдущей операции. В нашем случае в ней находится очередная строка из исходного файла. Если в этой строке есть подстрока , то начинает работать блок в фигурных скобках. Теперь нужно вычленить из $_ дату, увеличить ее на один день и вставить обратно. С помощью конструкции /(\d\d)\.(\d\d)\.(\d\d\d\d)/; производится "вычленение". Символ '\d' означает любую цифру, символ '\.' - точку. Фрагменты строки, соответствующие частям регулярного выражения, заключенным в скобки, сохраняются в переменных $1, $2, $3, ..., $n в порядке их следования. Таким образом, теперь в переменной $1 хранится "22", в $2 - "07" и в $3 - "2000". Увеличение даты на единицу производится с помощью функции IncreaseDate, написанной специально для этого:
Разберемся, как работает такая конструкция. Perl позволяет собирать значения и переменные в списки. Для этого служит оператор списка - пара круглых скобок. Списки можно присваивать друг другу:
сортировать, инвертировать, выделять из них подсписки. Имеются функции, превращающие список в строку и наоборот. К элементам списка можно обращаться, используя квадратные скобки:
Однако если такое обращение нужно делать неоднократно, то лучше сохранить список в массиве:
Значения параметров при вызове IncreaseDate объединяются в список, который копируется в массив с именем @_, доступный внутри функции. В конце IncreaseDate после всех необходимых вычислений выполняется оператор return $d, $m, $y; формирующий список значений функции, который присваивается списку ($d, $m, $y) в вызывающей программе. Таким образом, благодаря спискам функция может возвращать несколько значений. Вероятно, вы уже заметили, что имена переменных начинаются со знака $ или @. С "доллара" должны начинаться скалярные переменные: строки, числа и ссылки, - а с @ - массивы. Это нужно для того, чтобы избегать конфликтов с зарезервированными именами Perl. Скалярные переменные не имеют определенного типа (за исключением ссылок, для которых тип проверятся). Perl определяет, какой тип данных хранится в переменой, основываясь на контексте. Там, где ожидается число, значение будет рассматриваться как число, где строка - как строка. В функции IncreaseDate строки превращаются в числа, поэтому если номер дня или месяца - одинарное число, то перед ним теряется ноль (например, из строки "07" получается число 7). В пятой и шестой строках потерянные нули восстанавливаются, при этом используется еще одно средство Perl - интерполяция строк. Когда в строке, заключенной в двойные кавычки, есть имя переменной, на его место автоматически подставляется (интерполируется) значение этой переменной. В седьмой строке программы происходит замена вчерашней даты на сегодняшнюю. С помощью регулярного выражения \d\d\.\d\d\.\d\d\d\d в значении переменной $_ отыскивается дата и заменяется на $d\.$m\.$y, где вместо имен переменных подставляются их значения, так же как при интерполяции строк. Остальной текст в $_ сохраняется без изменений. Наконец, мы записываем в новый файл исправленную строку, функция print по умолчанию выводит содержимое переменной $_, NEWFILE - дескриптор нового файла. Итак, первое изменение сделано. Надо сказать, что на написание этого фрагмента у меня ушло полдня, но зато потом все пошло как по маслу. Приведу следующий фрагмент программы, чтобы показать взаимодействие списка и массива. Строка, в которой нужно увеличить дату, выглядит так:
А вот фрагмент программы, который заменяет эту дату на новую, вычисленную ранее:
Чтобы заполнить массив, ему можно присвоить список, вместо того, чтобы присваивать значения поэлементно. $d, $m и $y были получены в предыдущем фрагменте. Ключевое слово my используется для создания переменной, которая должна существовать только внутри блока. Его лучше не забывать использовать, поскольку, как ни странно, все переменные Perl по умолчанию являются глобальными, прямо как в нумерованном Бейсике. Вообще, можно сказать, что Perl - это Бейсик на новом витке своего развития, стоит только посмотреть на значки в идентификаторах для обозначения типа и на множество мощных целевых функций. Что касается предыдущего примера, то на самом деле можно было и не заводить массив, а сразу вытащить элемент из списка нужных по номеру:
Для написания остальной части программы осваивать еще что-то принципиально новое мне не пришлось. Я завела конфигурационный файл с несколькими секциями, разделенными фиксированными строками-сигнатурами. Этот файл и должен заполнять переводчик. А все изменения в html-файлах проводились по одной и той же схеме:
Вот фрагмент, с помощью которого в файл подставляется имя переводчика:
Здесь CONFIG - дескриптор конфигурационного файла. В нем на следующей строке после сигнатуры #English name of translator переводчик вписывает свое имя. Функция SearchString находит сигнатуру и ставит указатель чтения-записи перед строкой с именем переводчика. Эта строка считывается операцией readline, а функция chomp удаляет символ конца строки. Затем выполняется замена. \w - это любая буква, цифра или символ подчеркивания, плюс после \w означает, что такой символ может быть не один. Таким образом, с помощью \w+ \w+ задаются два слова (имя и фамилия в исходном файле). Запятая в начале и пара знаков "> - это элементы строки, идентифицирующие место замены (имя переводчика подставляется в тег META). В конце результат, сформированный в $_, выводится в файл. Часто на языке Perl можно в две строки записать то, что на C++ пишется в пятнадцать. В частности, код значительно сокращается за счет использования специальной переменной $_. Для многих функций она является аргументом по умолчанию, и многие функции записывают в нее результат своей работы. Поэтому мне не приходилось заводить лишние вспомогательные переменные. А например, благодаря определению типа из контекста при пересчете даты мне не требовалось переводить строку в число и обратно. В Perl очень удобно работать с массивами. Массивы не имеют фиксированного размера. Текущий размер определяется максимальным задействованным номером элемента, который хранится в переменной $#имя_массива, и поэтому нет необходимости передавать его из подпрограммы в подпрограмму. Помимо традиционного индексного доступа для их изменения можно использовать функции push, pop. Функция push добавляет один или несколько элементов в конец массива:
Функция pop удаляет из массива последний элемент. Функции shift и unshift делают то же самое, но с началом массива. Фактически, массивы объединяют в себе возможности стеков, деков, очередей и линейных списков. Массивы можно сращивать, получать их срезы, а также сортировать и инвертировать с помощью специальных функций. С помощью функции map можно выполнить один и тот же блок кода над всеми элементами массива (или списка). В Perl имеется еще такая структура данных, как хеш или ассоциативный массив. Хеш обеспечивает доступ к данным не по числовому, а по произвольному скалярному индексу. Это очень удобно для обработки списков ключевых параметров и ведения несложных баз данных. В частности, благодаря этому Perl стал так популярен при обработке параметров CGI-запросов. Кроме того, в Perl много функций для работы с файлами, например поиск по шаблону. В отличие от традиционных функций С findfirst и findnext, на Perl можно одной операцией сразу получить массив имен файлов, соответствующих маске. Удобство работы с файлами - это еще один довод в пользу утверждения, что Perl целесообразно использовать для сопровождения web-сайта. Пусть, например, вам надо поставить копирайты в конце каждого файла или изменить везде какой-то элемент дизайна. Программа, выполняющая это, пишется за пять минут. Если такие задачи возникают достаточно часто, то имеет смысл постепенно накапливать свою библиотеку несложных текстовых утилит, написанных на Perl. Благодаря использованию высокоуровневых операций и специальных переменных Perl-код получается коротким, а программу становится легче не только писать, но и сопровождать. Надо только не переусердствовать с компактностью записи. Если не знать в этом меры, программа превращается в ребус, разгадать который труднее, чем написать заново. На первичное освоение языка и написание моей программы вместе ушло всего около двух дней. При использовании уже знакомого С++, возможно, времени бы ушло не больше, но зато в дальнейшем аналогичные программы я смогу создавать намного быстрее. Теперь несколько слов об отладке. Отладчиком, который есть в ActivePerl, я не пользовалась, так как мне это просто не понадобилось. Все синтаксические ошибки находились элементарно, а семантические сразу были видны по результатам работы программы. Единственное неудобство - чтобы посмотреть, в каких строчках обнаружены синтаксические ошибки, приходится загружать сеанс MS-DOS, поскольку из-за пропадающего окошка не успеваешь прочитать диагностику интерпретатора. Отладчик запускается командой
Работает он с консолью. Возможности отладчика подробно описаны в документации, и они довольно обширны: можно проходить программу пошагово, ставить точки прерывания, просматривать значения переменных и многое другое. В Unix для того, чтобы сценарий сам вызвал интерпретатор, в первой строке нужно написать путь к нему, например:
В этой же строке можно указывать ключи, с которыми следует запускать интерпретатор. Например, хорошим стилем является запуск с ключом -w - вывод предупреждающих сообщений:
В Windows синтаксис "#!" не поддерживается, и эта строка просто игнорируется, соответствующая функциональность обеспечивается модификацией реестра при установке ActivePerl. А для того чтобы запускать интерпретатор с ключами, надо пользоваться командной строкой
В состав пакета ActivePerl входит также экспериментальная версия компилятора, но воспользоваться им мне не удалось. Напоследок надо сказать, что многие полезные возможности Perl остались даже не упомянутыми. Например, я ни словом не обмолвилась об огромном числе пакетов, расширяющих его функциональность. Но остановлюсь пока на том, что если вы имеете дело с созданием web-сайтов, то пользу от владения Perl трудно переоценить. Елизавета Лёлина |
|
2000-2008 г. Все авторские права соблюдены. |
|