Basic_perl
Perl = простота Basic + мощь Cи++
Михаил Евдокимов
МИР ПК #08/99
Популярность Internet растет с каждым днем, однако если раньше клиенты провайдеров в основном хотели получить доступ к системам электронной почты, то в последние два года наметилась тенденция к расширению спектра требуемых услуг. Теперь интересы пользователей не ограничиваются электронным почтовым ящиком и поиском какой-либо информации в WWW, они стремятся создавать собственные Web-страницы или даже Web-узлы. Новички организуют простые HTML-страницы, не обращая особого внимания на стиль и дизайн. Но со временем у них появляется желание сделать нечто эффектное и интересное для определенной части Internet-сообщества. И тогда новоявленному Web-мастеру приходится более полно изучить язык разметки гипертекста (HTML) и языки создания клиентских сценариев (VBScript1 и JavaScript)*.
Впоследствии у него могут появиться и новые запросы, — например пообщаться с посетителями своего узла. И в этом случае его уже не удовлетворит, если в тело страницы просто добавится <A HREF="mailto:writeme@site.ru">Webmaster</A>. Так, он решит, что неплохо было бы получить достаточно полную информацию о посетителях узла (имена, e-mail, телефоны, факсы и адреса), подсчитать количество посещений, собрать различные мнения и, наконец, создать базу данных, чтобы пользователям сеансов связи предоставить какие-либо определенные услуги. Но для всего этого знания HTML, VBScript1 и Java Script становится явно недостаточно, так как они описывают только технологию взаимодействия сервера и клиента.
Следовательно, нужно изучить языки для создания программ, работающих на сервере. Обычно их разрабатывают на Perl (Practical Extraction and Report Language — практический язык извлечений и отчетов), применяемом также для обработки потоков информации. Изначально предполагалось, что он будет использоваться в ОС Unix, но в дальнейшем Perl стали переносить на другие платформы, и сейчас он существует в самых разных версиях — для Unix, Windows, MS-DOS, OS/2, MacOS, Amiga, Atari ST, VMS, Plan 9 и др.
Для чего нужен Perl?
Perl предназначен для выполнения задач командных сценариев Unix в тех случаях, когда они слишком трудны, плохо переносимы или сложны для программирования на другом языке, например на Cи. Иногда содержимое Perl-программ выглядит для непосвященных как случайный набор символов, но, естественно, он имеет контрольную сумму, а каждый его символ — свое назначение.
Perl распространяется бесплатно, поэтому исходные тексты языка и многие двоичные файлы для использования вне Unix-архитектуры можно получить на одном из серверов сети CPAN (Comprehensive Perl Archive Network) по адресу http://www.perl.com/CPAN или на узле поддержки разработчиков по адресу http://www.basicnet.sonnet.ru/dounload.
Для создания и тестирования Perl-программ необходимы:
любой текстовый редактор, позволяющий сохранять файлы в ASCII-коде (например, встроенный редактор из оболочки FAR Commander);
программа конвертации ACSII-файлов в формат Unix-систем (в частности, редактор Castillo TextEditor, который можно свободно загрузить с сервера www.castillobueno.com);
интерпретатор Perl для отладки (у автора — Win32-версия Perl, доступная по адресу ftp.perl.com/pub/perl);
Web-сервер, поддерживающий работу Perl-программ (для проверки интерфейсных программ был применен Web-сервер OmniHTTPd 2.0 Professional, который можно загрузить с узла компании-разработчика по адресу www.omnicron.ab.ca/httpd);
FTP-клиент для загрузки файлов на сервер (больше всего для этого подходит CuteFTP 2.0, позволяющий устанавливать права доступа к файлам; его можно найти по адресу www.cuteftp.com);
любой Web-браузер (был использован MS IE 4.0).
Программы на языке Perl с расширениями .cgi или .pl должны находиться в специальном каталоге на Web-сервере, обычно называемом CGI-BIN. операционной системы Windows 9.x/NT, то не следует преобразовывать созданные файлы в формат систем Unix. А операционные системы семейства Windows не позволяют выставлять атрибуты доступа подобно Unix-системам — в них используется другая методика.
Структура Perl-программ
Perl-программы очень похожи на написанные на Cи/Cи++, возможно, потому, что сам язык был написан на Cи. Все Perl-программы состоят из операторов, имеющихся в файле и рассматриваемых в совокупности как одна большая программа, подлежащая выполнению. Но понятия «основной» (main) программы, как в языке Си, здесь нет. Комментарием в Perl является все, что следует за «решеткой» (#), вплоть до конца строки. Интерпретатор языка перед выполнением разбирает программу и компилирует в свой внутренний формат. Поэтому после ее запуска невозможно получить сообщение о синтаксической ошибке — это происходит только в процессе отладки программы в командной строке. В результате обеспечивается быстрое выполнение операций языка Perl после запуска.
Для написания программы можно использовать любой текстовый редактор. Так, в Windows это Notepad (Блокнот) или WordPad, в OS/2 — e или epm, в Unix — vi или emacs. Обычно лучшему пониманию языка способствует разбор небольшой программы (см. листинг 1).
Листинг 1. Пример программы на Perl
#!/usr/local/bin/perl @passwords = qw (inet basic net); print ”Enter the login: ”; $login = ; chomp ($login); if ($login eq ”Root”) { print ”Hello, Administrator! Glad to see you again!\n ”; } else { print ”Enter password: ”; $pass = ; chomp ($pass); $i = 0; $flag = ”no”; while ($flag eq ”no”) { if ($passwords[$i] eq $pass) { $flag = ”yes”; } elseif ($i <2) { $i = $i + 1; } else { print ”Incorrect password for $login, try again.\n”; print ”Enter password: ”; $pass = ; chomp ($pass); $i = 0; } } }
Интересной особенностью Perl является то, что программист не объявляет типы применяемых переменных. В первой строке описывается физический путь к выполняемому модулю интерпретатора (например, PERL.EXE), который должен начинаться со знака комментария (#):
#!/usr/local/bin/perl
В рассматриваемом случае массив @passwords включает три элемента: inet, basic, net. Команда qw(), заключающая их в скобки, освобождает от ввода кавычек, необходимого при использовании общепринятой конструкции вида
@passwords = (”inet”, ”basic”, ”net”);
Оператор print служит для вывода на экран символьной информации
print ”Enter the login: ”;
Приведенная ниже конструкция напоминает аналогичную по смыслу на языке Паскаль:
print ”Enter the login: ”; $login = ;
Следовательно, сначала располагается оператор вывода информации (print), а затем оператор ввода строки с терминала, выполняющегося в Perl с помощью считывающей одну строку данных конструкции . Переменная $login содержит и завершающий символ строки, например, Root будет введено как Root\n. Чтобы убрать лишний символ, требуется функция chomp, которая в качестве своего единственного параметра принимает скалярную переменную и удаляет из нее завершающий символ перехода на новую строку, если этот символ там присутствует:
chomp ($login);
Далее используется конструкция if-then-else:
if ($login eq ”Root”) { print ”Hello, Administrator! Glad to see you again!\n”; } else { ... }
Наличие значения переменной $pass среди элементов массива @passwords проверяет $passwords[$i] eq $pass.
Следующая ниже операция сложения
$i = $i + 1;
увеличивает текущее значение счетчика на одну позицию. В строке
print ”Incorrect password for $login, try again.\n”;
между кавычками помещается переменная $login, содержащая вводимое пользователем значение. В других языках программирования значения переменных обычно отделяются от данных. Например, в Basic эта строка будет иметь следующий вид:
print ”Incorrect password for”; login$ ; ” try again.”
Perl оперирует только двумя типами данных — ска
$a = 2; $b = 6; $c = $a . $b; # ”.” — операция конкатенации двух строк $d = $c / 2; print $d; # — результат равен 13
Аналогичный пример для строковых значений выглядит иначе (см. листинг 2).
Листинг 2
#!/usr/local/bin/perl -w # режим отображения # предупреждений # о возможности ошибок $who = ‘Michael Yevdokimov’; $where = ‘Moscow’; $what = ‘in MSATU’; print ”My name is $who,\n”; # представимся print ”I live in $where,\n”, ”I study $what there.\n”; # где учимся print ”\nSigned: \t$who,\n\t\t$where.\n”;
Для ввода скалярного значения используется дескриптор , причем следующая полная строка текста считывается до первого символа новой. Если же текущая строка еще не образована, то Perl останавливается и ждет до тех пор, пока не будут введены информация и вслед за ней символ перехода на новую строку.
Массив — это список скаляров. Название переменных такого типа начинается с символа ‘@’. Каждый элемент массива — это отдельная скалярная переменная, которой можно присваивать значение и затем использовать ее независимо от других. Однако можно присвоить значение и всем элементам массива сразу, например
@passwords = qw(inet basic net);
Проведя такую операцию, затем легко будет обращаться к каждому из скаляров с помощью индексных ссылок. В рассматриваемом примере $passwords[0] имеет значение inet, $passwords[1] — basic, а $passwords[2] — net. В качестве индекса может быть принято выражение, поэтому если присвоить $i значение 1 ($i = 1), то элементом массива $passwords[$i] будет basic. Поскольку каждый элемент массива — скаляр, при адресации ставится знак доллара, а не ‘@’. В отличие от других языков программирования, в массиве на Perl можно объединять скаляры разных типов данных. Если записать
@items = (20, ‘10.00’, ”диск”); print ”Купи мне $items[0] $items[2]ет за \$$items[1].\n”; то в результате получится текст «Купи мне 20 дискет за $10.00.»
Все массивы в языке — динамические. Не нужно беспокоиться о проблемах распределения памяти — интерпретатор все сделает за вас. Кроме того, массивы могут содержать подмассивы, поэтому можно создать подобную структуру:
@A = (1, 2, 3); @B = (4, 5, 6); @C = (7, 8, 9); @D = (@A, @B, @C);
Результирующий массив D будет содержать числовые значения от 1 до 9.
Большинство встроенных функций в Perl используют массивы как аргументы, например sort и join. Первая возвращает массив, но уже в отсортированном виде. Результатом операции
print sort (‘Beta’,’Gamma’,’Alpha’);
будет последовательность AlphaBetaGamma. Функция join имеет два входных параметра — строку и массив строк. Она возвращает строку, которая состоит из всех элементов массива, разделенных значением входной строки, т. е.
print join (‘:’,’Name’,’Address’,’Phone’);
и выдает на печать Name : Address : Phone.
Может возникнуть вопрос, как добавить к уже существующему массиву какой-нибудь элемент, не создавая при этом дополнительный массив. Так, массив @letters содержит элементы Beta, Gamma и Alpha. Если в него нужно добавить значение Tetta, то следует использовать возможности функции push (см. листинг 3).
Листинг 3
@b = (”Beta”, ”Gamma”, ”Alpha”); push @b, ”Tetta”; # добавим в массив @b новый элемент @w = sort @b; # отсортируем массив @b по алфавиту $c=0; # инициализируем переменную $c foreach (@w) { print ”$w[$c]\n”; # выведем отсортированные значения $c++; }
Как ранее указывалось, дескриптор возвращает в скалярном виде значение введенной строки. Примененный же к массиву, он каждому его отдельному элементу, вплоть до конца файла, присваивает значение очередной строки. Таким образом, если при выполнении программы ввести три строки и операцию конца файла [Ctrl + Z] или [Ctrl + D], то массив будет состоять из трех элементов, которые являются строками и заканчиваются символами перехода на новую строку.
Ассоциа является «Green», которому будет соответствовать элемент массива «Apple». Для лучшего понимания использования АМ следует сопоставить ключи с ID в таблицах реляционных баз данных, которые представляют собой практически одно и то же. Рассмотрим пример из листинга 4.
Листинг 4
%Folk = (‘BG’, ‘Bill Gates’, ‘MY’, ‘Michael Yevdokimov’, ‘BC’, ‘Bill Clinton’); %State = (‘BG’, ‘California’, ‘MY’, ‘Moscow’, ‘BC’, ‘Washington’ ); %Job = (‘BG’, ‘work in Microsoft’, ‘MY’, ‘write this article’, ‘BC’, ‘work as the President of USA’); foreach $person (‘MY’, ‘BG’, ‘BC’) { print ”My name is $Folk{$person},\n”, ”I live in $State{$person},\n”, ”I $Job{$person} there.\n\n”; }
Содержимое массивов можно представить и в другой форме, например
%Job = (‘BG’ => ‘work in Microsoft’, ‘MY’ => ‘write this article’, ‘BC’ => ‘work as the President of USA’);
Индексы и элементы массива можно заключать как в апострофы, так и в кавычки. Чтобы перебрать все значения АМ, нужно использовать оператор foreach. Он предназначен для организации циклов, как и некоторые другие, в частности while. Можно обращаться к ключам и значениям с помощью операторов keys и values.
Специальный ассоциативный массив %ENV хранит содержимое всех переменных, индексированных по имени. Так, $ENV{‘PATH’} возвращает текущее значение пути поиска. Существует также функция each, приводящая список, который состоит из двух элементов — ключа и значения. При каждом следующем вызове она возвращает новую пару, к примеру
while (($key,$value) = each %ENV) { print ”$key = $value\n”; }
Работа с файлами и каталогами
Для нормальной работы в Perl с файлами и каталогами следует запомнить несколько важных процедур (см. таблицу).
Дескриптор представляет собой особый вид символьных переменных (literal string). Дескрипторы файлов, так же как и метки, применяются без специального префиксного символа, поэтому их можно спутать с существующими или зарезервированными словами (для подпрограмм, команд и пр.). При программировании названия дескрипторов рекомендуется писать только прописными буквами. Во-первых, они легко различимы среди остального текста, во-вторых, благодаря этому программа будет правильно выполняться. Дескриптор обычно представляет собой «название» файла, на который ссылается пользователь. Как и при программировании на Basic, Паскале или Cи/Cи++, дескрипторы в Perl подобны переменным, присутствующим в синтаксисе операций открытия, закрытия, считывания или записи в файл. Подобно другим языкам, Perl также использует дескрипторы в операциях манипулирования содержимым файлов. Однако есть и другие варианты их применения.
Существует три разных способа открытия файла для проведения чтения (read), дополнения (append) и записи (write).
Режим чтения (Read) — самый простой. Синтаксис операции open следующий:
open (HANDLE,”filename.txt”);
Оператор open используется для открытия файла. В круглых скобках заключен дескриптор файла HANDLE. В дальнейшем при выполнении операций над файлом filename.txt и его содержимым на него будут приведены ссылки в программе. В кавычках стоит имя файла.
Для считывания информации из файла выполняется так называемая операция ромба, обозначаемая символами (<>):
open (HANDLE,”filename.txt”); while () { # Этот цикл будет считывать информацию из файла построчно }
Режим записи (Write) имеет следующий вид:
open (HANDLE,”>filename.txt”);
Отличие синтаксиса операций записи от синтаксиса чтения заключается лишь в том, что перед именем файла стоит символ «больше чем» (>). Этот знак сообщает, что следует создать указанный в кавычках файл и записать или обновить (если он уже существует) его содержимое. Чтобы записать в него информацию, нужно обратиться к помощи оператора print:
open (HANDLE,”>filename.txt”); print HANDLE ”Записать этот текст в файл...”;
Режим до попытке открыть файл для чтения выдается значение false, то это означает, что файла нет или доступ к нему запрещен. А когда при открытии файла для ввода информации это значение возвращается, то можно сделать вывод, что либо файл защищен от записи, либо невозможна запись в каталог или доступ к нему. Если затем программа завершит свою работу или файл заново откроется, то не нужно закрывать его после окончания работы с дескриптором — операция открытия файла закрывает ранее задействованный дескриптор. Тем не менее лучше все же закрыть файл с помощью операции close. Подобная структура является «хорошим тоном» при программировании:
close (HANDLE);
Отладка. Всякий раз при открытии файла разумно использовать вместе с оператором open оператор die. Бывает, что файл по какой-то причине нельзя правильно открыть. Программа вроде бы выполнилась, как требовалось, а в файл записалось вовсе не то, что ожидалось. В подобном случае оператор die прерывает выполнение программы и выдает сообщение об ошибке при открытии файла.
В синтаксисе совмещения open и die используется «логическое ИЛИ» (||):
open (HANDLE,”>>filename.txt”) || die ”Ошибка добавления в файл filename.txt $!\n”;
Функция die, название которой можно перевести с английского как «откройся или умри», прерывает выполнение программы. Выдается сообщение об ошибке, а также информация о том, что ее вызвало. Perl сохраняет сведения о последней системной ошибке в специальной переменной $!. Если после функции die вставить $!, то от ОС будут получены дополнительные данные, которые помогут отладить программу.
Проверка файлов. Теперь можно открыть дескриптор файла для записи, уничтожив имеющийся файл с таким же именем. Но для этого сначала нужно проверить, существует ли файл с таким именем, чтобы не стереть какую-либо важную информацию. При этом следует использовать следующую конструкцию:
$filename = ”filename.txt”; if (-e $filename) { print ”Файл $filename уже существует\n”; } else { print ”Файл $filename не найден\n”; }
И для нескольких файлов можно за один раз выяснить, существуют ли они, просто уничтожив первую строку предыдущего примера и заменив вторую конструкцией
if (-e ”filename.001” && -e ”filename.002”) {
Есть множество других операций для проверки файлов. Например, чтобы убедиться в наличии какого-либо файла и возможности чтения из него, нужно вместо операции -e выполнить -r, а в случае требования возможности записи —w. Можно проверить один и тот же файл на доступность чтения и записи информации, выполнив следующее:
$filename = ; chomp ($filename);# убрать символ новой строки if (-r $filename && -w $filename) { # файл существует, мы можем читать из него # и записывать в него . . . }
Чтобы определить возможность чтения для целой группы файлов с одинаковым расширением, можно использовать конструкцию:
@files = <*.txt>; foreach (@files) { print ”$_ is readable\n” if -r; }
При большинстве подобных проверок, а их около 20, возвращается значение true или false.
Отличия от Win32. При работе на Perl под управлением Windows существуют некоторые нюансы, о которых следует знать. Во-первых, нужно указывать полный путь к файлу (вместе с именем диска), над которым будут выполняться какие-либо действия, например
open (HANDLE,”c:/scripts/newfile.txt”) || die ”Error opening c:/scripts/newfile.txt $!\n”; ... close (HANDLE);
Во-вторых, блокировка файла происходит иначе, чем в Unix-системах. При использовании Windows 9.x эта операция вообще не поддерживается, а в Windows NT выполняется весьма своеобразно — перед выполнением команд копирования или изменения имени файла нужно удостовериться, что вы уже закрыли его. Иначе они просто не выполнятся.
ОБ АВТОРЕ
Михаил Евдокимов — программист, координатор проекта Developers Support Site; е-mail: flanker@sonnet.ru; http://www.basicnet.sonnet.ru