А что дальше?
По идее, если все вы сделали правильно, то скрипт установлен - его можно запускать и, возможно, настроить какие-нибудь опции, но все это происходит уже в привычной форме, через окно броузера. Очень вероятно, что скрипт не запустится и выдаст ошибку - проверьте, правильно ли вы все сделали! Все мои ошибки, возникали из-за моей невнимательности или когда я, изображая Билл Гейтса, пытался маааленько изменить код - а Perl то я не знаю ;-)
В общем, что-то я поставил, кое с чем еще извращаюсь - зайдите ко мне на сайт, посмотрите!
Альтернативные шаблоны
Вы можете задать несколько альтернативных шаблонов, используя символ | как разделитель. Альтернативные шаблоны позволяют превратить процедуру поиска из однонаправленного процесса в разветвленный: если не подходит один шаблон perl подставляет другой и повторяет сравнение, и так до тех пор, пока не иссякнут все возможные альтернативные комбинации. Например, следующий фрагмент проверяет, не ввел ли пользователь "exit", "quit" или "stop":
while (<>){ if(m/exit|quit|stop/){exit;} }
Чтобы было ясно, где начинается и где заканчивается набор альтернативных шаблонов, их заключают в круглые скобки - иначе символы, расположенные справа и слева от группы шаблонов, могут смещаться с альтернативыми шаблонами.
В следующем примере метасимволы ^ и $ обозначают начало и конец строки и отделяются от набора альтернативных шаблонов с помощью скобок: while (<>){ if(m/^(exit|quit|stop)$/){exit;} }
Альтернативные варианты перебираются слева направо. Как только найдена первая альтернатива, для которой выполняется совпадение с шаблоном, перебор прекращается. Участки шаблона, заключенные в круглые скобки, выполняют специальную роль при выполнении операций поиска и замены. Если символ \ находится в квадратных скобках, он интерпретируется как обычный символ. Поэтому если вы используете конструкцию шаблона вида [Tim|Tom|Tam], то она будет эквивалентна классу символов [Tioam|]. Точно так же большинство других метасимволов и команд, специфичных для регулярных выражений - в частности, квантификаторы и мнимые символы, описанные в двух последующих разделах, - внутри квадратных скобок превращаются в обычные символы или escape-последовательности текстовых строк.
Анимация
Когда говорят о каком-то популярном сайте,то частенько к преимуществам относят и анимацию. Действительно,когда изображение изменяется (и особенно к месту ;)),то это смотрится и пользователю нравится.
Говоря об анимации нужно сразу отметить что нет лучшего способа. Анимацию можно сделать ДЕСЯТКАМИ Способов,каждый хорош в своей области применения. Я перечислю только некоторые из них,которые чаще всего применяются:
Самый простой,но наименее функциональный способ это GIF с анимацией.
Потом можно воткнуть анимационный файл MPEG или AVI они более отражают суть анимации, но имеют недостаток,что для проигрывания их на некоторых браузерах нужны специальные подключаемые модули.К тому же они не интерактивны.
Можно реализовать анимацию в рамках Java-апплета,когда апплет находясь на страничке сам перерисовывается со временем.
Таким же интерактивным средством служит обращение к массиву document.images[] из JavaScript.Достоинство-помимо интерактивности,полная интегрированость с HTML -станичкой.Но может как и предыдущий использоваться только с относительно новыми браузерами,которые поддерживают Java и JavaScript.
В общем в каждом случае выбор остается за вами.Вам решать насколько тот или иной способ хорош в вашей ситуации.Я же познакомлю вас с еще одним.
Вы даже уже были знакомы с этим способом,когда я вам рассказывал о nph- скриптах Теперь когда вы уже так много знаете,можно модифицировать тот , добавив в него вызов картинки по случайному принципу:
#!/usr/bin/perl #nph-animate2.cgi $delay=3; @files = qw(img0.gif img1.gif img2.gif img3.gif);
select (STDOUT); $|=1; #autoflush mode on #Generate header print "HTTP/1.0 200 Okay\n"; print "Content-Type: multipart/x-mixed-replace;boundary=myboundary\n\n"; srand; print "--myboundary\n"; while(1){ $file=$files[int(rand($#files))]; #random file print "Content-Type: image/gif\n\n"; open(PIC,"$file"); print <PIC>; close(PIC); print "\n--myboundary\n"; sleep($delay); }
Конечно одно из самых примитивных применений такой системы.Более мощным примером могло бы послужить отслеживание на сервере какого-нибудь периодически изменяющегося файла и пересылка пользователю обновленной версии.
Такая Система применяется например в Чате,при появлении новых сообщений. Чатовая система достаточно сложна для этого пособия и я не стал сюда ее включать.Однако,если вам очень интересно,то я с удовольствием пришлю ее вам.
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
Что можно делать с помощью cookie?
Сами по себе cookies не могут делать ничего, это только лишь некоторая
информация. Однако, сервер может на содержащуюся в cookies информацию.
Например, в случае авторизованного доступа к чему либо через WWW,
в cookies сохраняется login и password в течение сессии, что позволяет
не вводить их при запросе каждого запаролированного документа.
Другой пример: cookies могут использоваться для построения персонализированных
страниц. Чаще всего встречается такое - на некотором сервере Вас
просят ввести свое имя, и каждый раз, когда Вы заходите на первую
страницу этого сервера, Вам пишут что-то типа "Hello, your_name!".
На использовании cookies также часто строят функцию оформления
заказа в онлайновых магазинах, в частности, в Амазоне, такая своеобразная
виртуальная корзина покупателя, как в обычном реальном супермаркете.
Что такое cookie?
Cookie является решением одной из наследственных проблем HTTP
спецификации. Эта проблема заключается в непостоянстве соединения
между клиентом и сервером, как при FTP или Telnet сессии, т.е.
для каждого документа (или файла) при передаче по HTTP протоколу
посылается отдельный запрос. Включение cookie в HTTP протокол
дало частичное решение этой проблемы.
Cookie это небольшая порция информации, которую сервер передает
клиенту. Клиент (броузер) будет хранить эту информацию и передавать
ее серверу с каждым запросом как часть HTTP заголовка. Некоторые
cookie хранятся только в течение одной сессии, они удаляются после
закрытия броузера. Другие, установленные на некоторый период времени,
записываются в файл. Обычно этот файл называется 'cookie.txt'.
Что такое файловые манипуляторы, и с чем их едят
Доступ к файлам осуществляется с помощью файловых манипуляторов, которые представляют собой так сказать синоним файла. Они не являются переменными, а поэтому их нельзя непосредственно присваивать другим переменным или передавать в функции (для этого нужно, что называется, пойти другим путем).
Есть и стандартные Перловские файловые манипуляторы. Они называются STDIN (стандартный ввод), STDOUT (стандартный вывод) и STDERR (стандартный поток ошибок). Например параметры скрипту из формы передаются именно через STDIN (при условии использования метода POST).
Если понадобится создать копию файлового манипулятора (не файла, а только манипулятора по которому осуществляется доступ к файлу), то можно воспользоваться функцией open (о ней подробнее поговорим позже).
Пример: open(FILL,"file.txt"); open(FAIL,"
О присваивании переменным файловых манипуляторов:
$handle=*FILL; или передать его в функцию: some_sub(*FILL);
И под конец скажу, что файловые манипуляторы в Perl используются не только для связи с, собственно, файлом. Они могут быть связаны с каким-нибудь процессом, сокетом и т.д. Но это не входит в тематику статьи.
Data Bases
Q: Как работать из perl с базами данных?
A: DBI. Это унифицированный программный интерфейс, придуманный для того, чтобы
с разными базами можно было работать одинаково (за исключением, конечно,
специфики самих SQL-запросов). Устанавливаете модульDBI (Data Base
Interface), ищете и устанавливаете DBD::something (Data Base Driver) -
драйвер-связку между DBI и нужной вам базой. Oracle, MySQL, PostgreSQL и куча
других СУБД поддерживаются DBI+DBD. Читаете perldoc DBI
Доступ к элементам
Проверка хэша на наличие элемента:
%hash = (
'шляпа' => 'серая',
'водка' => 'горькая',
'вобла' => 'вкусная');
if(exists($hash{"дождь"})){
print "Элемент найден";
}
else{
print "Элемент не найден";
}
Удалить элемент из хэша можно при помощи функции delete:
%hash = (
'шляпа' => 'серая',
'водка' => 'горькая',
'вобла' => 'вкусная');
delete($hash{"шляпа"});
if(exists($hash{"шляпа"})){
print "Элемент найден";
}
else{
print "Элемент не найден";
}
Функция delete может вызываться для среза хэша, что приводит к удалению всех указанных ключей:
delete @hash{'шляпа','водка','вобла'}; см. perlfunc(1)
Если нужно найти совпадающие ключи или не входящие в другй хэш, то надо организовать перебор ключей хэша при помощи keys и проверять, если ли текущий ключ в другом хэше. Поиск совпадающих ключей:
my @common = ();
foreach(keys %hash1){
push(@common, $_) if exists $hash2{$_};
}
Поиск ключей, отсутствующих в другом хэше:
my @test = ();
foreach(keys %hash1){
push(@test, $_) unless exists $hash2{$_};
}
Если keys вызывается для хэша, ключи которого представляют собой ссылки, то возвращаемые ей ссылки не работают. Ключи преобразуются в строки, т.е. интерпретируются так, словно они заключены в кавычки, при работе со ссылками они теряют свои свойства. После преобразования в строку ссылка имеет вид
Class::Somewhere=HASH(0x72048)
ARRAY(0x72048)
Преобразованную ссылку нельзя вернуть к прежнему виду, т.к. она из ссылки превратилась в строку. Нужно создать специальный хэш, ключами которого являются ссылки, преобразованные в строки, и значениями - настоящие ссылки.Можно воспользоваться модулем Tie::RefHash. Пример показывает использование объектов ввода/вывода для работы с файловыми манипуляторами.
use Tie::RefHash;
use IO::File;
tie %name, "Tie::RefHash";
foreach $filename("/etc/termcamp/", "/vminux", "/bin/cat"){
$fh = IO::File->("<$filename") or next;
$name{$fh} = $filename;
}
print "open files: ", join(", values %name", "\n");
foreach $file(keys %name){
seek($file, 0, 2);
printf("%s is %d bytes long.\n", $name{$file}, $tell{$file});
}
Если в качестве ключа использована неопределенная величина undef, то она преобразуется в пустую строку. undef является вполне допустимым значением в хэше. Но при выборке значения для ключа, отсутствующего в хэше perl выдаст undef. Проверить наличие ключа можно так: exist($hash{$key}); определенность ассоциированного значения: defined($hash{$key}); истинность: if($hash{$key});. Иногда undef нужно сохранять в кэше, т.е. ключ есть, но с ним не связано ничего полезного, например программа, определяющая размер файлов из переданного списка:
%name =();
while(<>){
chomp;
next if exist $name{$_};
$name{$_} = -s $_;
}
Этот код позволяет пропустить несуществующие и нулевые файлы, но записанные в исходном списке.
Хэши с несколькими значениями, ассоциированными одним ключом. Т.к. скалярные величины, содержащиеся в хэше, могут быть ссылками(которые могут быть скалярами), то ассоциировать несколько значений одним ключом можно сохранив в $hash($key) ссылку на массив со значениями, ассоциированными с ключом $key. Операции с хэшами(вставка, удаление, перебор и проверка существования(undef)) переписываются для операций с массивами(push, splice и foreach). Пример, реализующий вставку в хэш(обрабатывает выходные данные команды who(1) и выводит краткий список пользователей с терминалами, на которых они зарегестрированы):
%ttys=();
open (WHO, "who|");
while(){
($user, $tty) = split;
push(@ {$ttys{$user}}, $tty);
}
foreach $user (sort keys %ttys){
print "$user: @{$ttys{$user}}\n"
}
в строке push содержится версия $tty{$user} = $tty для многозначного хэша. Все имена терминалов интерполируются в строке print @{$ttys{$user}}.
Пример программы, которая на название предмета выдает его свойство и наоборот:
#!/usr/bin/perl -w
$vziat = shift @ARGV or die $!;
%svojstvo = (
"malina" => "vkusnaia",
"svekla" => "krasnaya",
"kozmodrom" => "nebolshoy",
"magazin" => "dvuhetagnij");
%predmet = reverse %svojstvo;
if (exists $svojstvo{$vziat}){print "$vziat," ", $svojstvo{$vziat}\n";}
elsif (exists $predmet{$vziat}){print "$vziat," ", $predmet{$vziat}\n";}
например если ввести в терминале:
bash-2.03$ ./1.pl malina
то скрипт выдаст:
malina vkusnaia
или
bash-2.03$ ./1.pl vkusnaia
vkusnaia malina
В чем различие delete и undef для хешей?
Хеши являются парами скаляров, первый - ключ, второй значение.
Ключ может быть строкой, в то время как значением хеша может
быть любой вид скаляра: строка, число или ссылка. Если ключ
содержится в хеше, то exists($key) возвратит истину. Значение для
какого-то конкретного ключа может быть undef'ом, и $array{$key}
возвратит так-же undef, но exists($key) возвратит истину.
Иными словами в хеше может быть реализована связка ('$key', 'undef')
В качестве примера можно привести следующую таблицу %ary:
keys values
+------+------+
| a | 3 |
| x | 7 |
| d | 0 |
| e | 2 |
+------+------+
Этот хеш выглядит примерное так:
$ary{'a'} true
$ary{'d'} false
defined $ary{'d'} true
defined $ary{'a'} true
exists $ary{'a'} true (perl5 only)
grep ($_ eq 'a', keys %ary) true
Если теперь сказать
undef $ary{'a'}
То таблица будет читаться следующим образом:
keys values
+------+------+
| a | undef|
| x | 7 |
| d | 0 |
| e | 2 |
+------+------+
И теперь логические состояния в хеше уже немного другие,
изменения показаны регистром
$ary{'a'} FALSE
$ary{'d'} false
defined $ary{'d'} true
defined $ary{'a'} FALSE
exists $ary{'a'} true (perl5 only)
grep ($_ eq 'a', keys %ary) is true
Отсюда следует вывод, что можно держать значение undef'ом,
но ключ всегда должен быть определен.
Теперь рассмотрим операцию удаления элемента из хеша:
delete $ary{'a'}
после этого таблица будет выглядеть так:
keys values
+------+------+
| x | 7 |
| d | 0 |
| e | 2 |
+------+------+
Состояния элементов в хеше уже другие,
изменения показаны, как и в предыдущем примере, различающимся регистром.
$ary{'a'} is false
$ary{'d'} is false
defined $ary{'d'} is true
defined $ary{'a'} is false
exists $ary{'a'} is FALSE (perl5 only)
grep ($_ eq 'a', keys %ary) is FALSE
from:
Q: Как послать e-mail из скрипта?
A: Воспользоваться утилитой sendmail (есть на всех unix системах)
Пример:
open MAIL, '|/path/to/your/sendmail -t';
print MAIL "From: mail@my.host\n",
"To: webmaster@my.host\n",
"Subject: Mail from perl script!\n\n",
$message;
close MAIL;
Q: Sendmail это хорошо, а как из под виндов?
A: Есть sendmail под винды, платный, бесплатный и даже от M$.
Q: Как послать по e-mail письмо с аттачем?
A: rfc2045, rfc2046, perldoc Mime::Lite
Q: Можно ли проверить, существует ли определенный e-mail адрес?
A: В общем случае - нельзя. Проверка существования и функционирования домена,
в котором этот email прописан, в сущности ничего не дает. Остается
отталкиваться от конкретных требований. Hапример, послать e-mail на этот адрес
и попросить ответить, проверив ответ.
Функции, использующие регулярные выражения
Фактически, есть три функции, которые в качестве разделителя могут использовать регулярные выражение: split, grep, map и еще можно воспользоваться специальными операторами ... и .. и используемыми совместно с ними условиями if, unless и просто логическими операторами.
Где ставить cgi скрипты?
На своем сервере, надо чтобы он поддерживал CGI, кроме того, узнайте полный путь к папке cgi-bin (что-то типа "home/home-webservis/kakadu/public_html/cgi-bin" не путайте это с URL, в приведенном случае, URL папки CGI будет выглядеть обычно, т.е. примерно так: http://kakadu.al.ru/cgi-bin) - обо всем этом вы сможете прочитать в FAQ вашего хостинга, посмотрите путь к почтовой папке (например /usr/sbin/sendmail), и ГЛАВНОЕ, узнайте путь к Perl, по умолчанию считается #!/usr/bin/perl, но могут быть вариации… "
Где взять скрипты?
Надеюсь, что все любители отвечать на этот вопрос примерно так: "Потратить часок-другой и написать самому!" уже давно прекратили чтение этой статьи, а оставшиеся, как и я, довольно смутно представляют себя в роли программиста… Не унывайте! Я уже говорил в начале статьи, что существует множество бесплатных скриптов и я не соврал! Более того, таких скриптов очень много, конечно они довольно часто уступают по возможностям скриптам платным, но, к счастью, из-за большого их количества часто удается подобрать то, что нужно. Вот несколько наиболее интересных, на мой взгляд, ссылок на скриптовые ресурсы:
1. - мне действительно нравится этот каталог! На первый взгляд, скриптов тут не очень-то и много (всего пару сотен), но должен признать, что тут подобраны наиболее интересные и популярные скрипты, существующие в Сети. Да и сайт русскоязычный, что для многих весьма важно.
2. - а вот это мировой гигант, собравший не только perl cgi скрипты, но и скрипты, написанные на языках C/C++, Python, есть там и PHP скрипты, и Java, и DHTML… Вам же следует пройти в раздел Perl (большинство скриптов написано на этом языке) , после чего вы сможете лицезреть несколько тысяч скриптов! Там перечислены не только бесплатные, но и платные скрипты (но об этом там написано, думаю, что слово "Free!" никто не пропустит). Все скрипты довольно удачно разбиты по категориям, и там нетрудно найти то, что надо. Всем рекомендую!
Есть и еще множество каталогов скриптов, но, мне кажется, они все уступают HotScripts.com или WebScript.ru (если говорить о русскоязычных ресурсах), как по подборке скриптов, так и по удобству пользования сайтом. Среди достойных внимания назову еще :
The CGI Resource Index () - весьма нравился мне, пока я не нашел Hotscripts.com
CGI City ( ) - не самая удобная навигация, но попадаются довольно интересные скрипты.
FreePerlCode ( ) - Как и следует из названия, здесь вы можете получить CGI PERL скрипты - они бесплатные, их можно скачать.
КОНЕЦ!
Green Kakadu, прямая трансляция с сайта Гнездо: каталог для вебмастеров [].
P.S. Все испробовал на себе - при проведении эксперимента пострадал только один Kakadu, т.е. Я! Исход, к счастью, не смертельный. Пишите письма!
[ Распечатать ]::[ ]::[ ]
Генерация ответа
Большую часть того что нужно знать о генерации ответа,я сказал в разделе .Нет,не угадали! Я не буду сдесь говорить о всяком дизайне того что вы выдаете.Этому вы успели напрактиковатся на HTML -страничках.
Я поговорю о MIME (Multipurpose Internet Mail Extension).И о разных браузерах.
Стандарт MIME появился в электронной почте (e-mail) потому что остро стала проблемма пересылки по e-mail различных данных в различных форматах.Так как HTTP тоже работает с различными типами данных то поэтому тоже использует MIME для своих нужд. Типы MIME состоят из Типа и подтипа (например text/plain,где text-указывает на наличие текстового содержимого,а plain-уточняет его как простой текст) приведеный ниже список (он далеко не полн,типов MIME огромное количество) описывает некоторые часто встречающиеся типы.: text/html text/plain text/richtext image/gif image/jpeg image/tiff audio/basic audio/32kadpcm audio/ video/mpeg video/quicktime multipart/mixed multipart/alternate multipart/ application/octet-stream application/msword application/postscript message/digest
Информация о MIME больше возможно пригодится вам в том случае если вы собираетесь работать из ваших скриптов с электронной почтой,но и для WWW она не повредит. Онобенно знание Content-Type:
Content-Type:
Состоит из типа и подтипа типы могут быть как стандартные так и экспериментальные начинающиеся с префикса 'x-':
text
Текстовые данные.Первым подтипом который включен сюда это plain,что значит простой текст. сюда же включен самый ходовой формат html .У типа text как и у многих типов могут быть параметры,главным из них является charset он как раз и указывает на раскладку символов, которая применена в тексте, так что если вы хотите указать браузеру какую раскладку применять, то просто укажите charset:
Content-Type: text/plain; charset=us-ascii
Content-Type: text/html; charset=iso-8859-1
Content-Type: text/html; charset=koi8-r
multipart
Данные которые могут состоять из нескольких частей,различных типов данных.Поэтому параметром multipart служит boundary, позволяюший указать разделитель.Каждый фрагмент в многочастевом сообщении имеет свой Content-Type: (Он может быть также multipart,т.е. допускаются вложеные multipart,главное чтоб boundary были разными).В электронной почте применяется больше multipart/mixed (основной подтип) и multipart/alternative (Он отличается тем что показывается одна из альтернатив,например сообщение шлется в простом и HTMLом форматах,и почтовая программа показывает либо часть,которую она способна отобразить). В WWW -програмировании распостранен x-mixed-replace ,который означает что следующая часть должна заменить предыдущую после подгрузки, что применяется для анимации(см.Пример с анимацией).
Теперь о разделителе,его надо выбирать так,чтоб он не встретился где-то в данных (т.е. что-то вроде "diUr344rnmvforgefvrg923rghyj2").Когда вы задали разделитель,например boundary="boundary" то когда закончилась одна часть,вы должны выдать строку --boundary,последняя часть --boundary--, причем эти разделители должны быть на отдельной строке,а не сливаться с текстом:
Пример:
MIME-Version: 1.0 Content-Type: multipart/alternative; boundary="w23renff491nc4rth56u34-9449"
--w23renff491nc4rth56u34-9449 Content-Type: text/plain; charset="koi8-r"
Hello,World!! --w23renff491nc4rth56u34-9449 Content-Type: text/html; charset="us-ascii"
<H1>Hello,Word!!</H1> <HR> <FONT size=+1 color=red>Hello people!</FONT> --w23renff491nc4rth56u34-9449--
message
Представляет инкапсулированое почтовое сообщение.Используется в e-mail ,а не в WWW.
image
Некоторое Графическое изображение.(чаще всего image/gif и image/jpeg)
audio
Аудиоданные.
video
Видеоданные.
application
бинарные данные какого-нибудь приложения.В том случае если данное приложение может быть запущено,Браузер запускает его.Например при поступлении данных application/msword Браузер спросит,нужно ли запустить Word для просмотра досумента.При отсутствии нужного приложения браузер спросит в каком файле сохранить данные.Подтип octet-stream как раз и означает поток байт информации,который и используется по умолчанию.(К сожалению не все так гладко,известен глюк в Netscape Navigator'е который вместо того чтоб сохранить application/octet-stream
пытается его показать как text/plain что если это сгенерировано из CGI,ни к чему хорошему не приводит ;(()
Что касается application ,то Вы можете тут смело извращатся,используя x- типы данных,
Например application/x-fuck-to-netscape-navigator. ;)))))
Часто используемый параметр name позволяет указать имя файла.Например:
Content-Type: application/msword; name="readme.doc"
Что полезно при полученнии файлов через HTTP,причем этот параметр может применятся и для других типов таких image или audio ,Например:
Content-Type: image/gif; name="myfoto.gif"
Content-Transfer-Encoding:
Применяется больше в системе электронной почты и обозначает метод кодирования, которым были закодированы данные при передаче сообщения.Например:
7bit 8bit quoted-printable base64 binary x-типы
MIME-Version:
Указывает версию MIME .
Теперь поговорим о разных браузерах вы знаете что браузеры бывают разные,разных версий на разных платформах, поддерживают и не разные тэги и глюки у них тоже разные.....;((( .
Это могло попортить много нервов WEB-дизайнерам и конечно же нам ,CGI-програмистам. Профессионально написаный сайт от просто хорошего отличается тем что хорошо выглядит Не только на экране того браузера,которым пользуется сам его автор,а на других тоже.
Если вы используете JavaScript для своих страничек,то вы уже наверно использовали (или хотя бы вам в голову приходила мысль использовать)свойства navigator.AppName navigator.AppCodeName navigator.appVersion navigator.userAgent:
<SCRIPT language="JavaScript"> if(navigator.AppName=="Netscape"){ /*Сделать чо-нибудь специфичное для Netscape*/ } else if(navigator.AppName=="Microsoft Internet Explorer"){ /*Сделать чо-нибудь специфичное для Explorer*/ } else{ /*Не делаем специфичных вещей-хрен его знает с каким браузером мы имеем дело*/ } </SCRIPT> или <SCRIPT language="JavaScript"> if((navigator.AppName=="Netscape")&&(parseFloat(navigator.appVersion)
Ну не волнуйтесь вы так ,мы CGI-программисты не в самых худших условиях на этот счет. Вспомните о том что браузер сам при запросе посылает вам данные о себе и о своей версии. И делает он это для того,чтобы эту информацию можно было учесть.
В запросе он указывает User-Agent: которое и попадает на сервере в переменную среды HTTP_USER_AGENT ,которую и можно использовать.
Например если в ней содержится Mozilla/3.01Gold (Win95;I) то значит вы имеете дело с Netscape (Mozilla-кодовое название Netscape Navigator'а),версии 3.01Gold и далее после имени и версии может следовать необязательная информация ,например как в приведеном примере о платформе Win95 и о том является ли версия U -для США (USA) или I -международной(International). Напомню,что такая информация необязательна.(То есть если браузер послал информацию User-Agent:
то гарантировано расчитывать вы можете только на Название/Версия).
Ну вот я слишком много развел демагогии,пора переходить к практическим примерам.
Допустим ваш скрипт генерирует какие-то тэги,которые слишком старые браузеры не поддерживают,причем без них не обойдешся,они составляют всю 'изюминку' сайта.
#!/usr/bin/perl #oldbrowser.cgi print "Content-Type: text/html\n\n"; if(defined ($ENV{'HTTP_USER_AGENT'})){ $browser=$ENV{'HTTP_USER_AGENT'}; ($vers)=($browser=~/\/(\d+\.\d+)/); if(($browser=~/mozilla/i)&&($vers
Ну уже почувствовали,насколько это здорово.А вот еще примерчик.Это из разряда того, что тэги бывают разные.Например в Explorer есть тэг BGSOUND предназначеный для проигрывания музыки на страничке.(В Netscape этого тега нет,и поэтому для втыкания музыки приходится использовать подключаемые модули plugin).Мутится с этими Плугинами Вам в облом,а хочется побаловать человека хорошей музыкой,если браузер позволяет.
... ... if($ENV{'HTTP_USER_AGENT'}=~/msie/i){ print "<BGSOUND src=\"jmj00.mid\">"; } elsif($ENV{'HTTP_USER_AGENT'}=~/mozilla/i){ #Оставлю сдесь коментарий,что воткну что-нибудь типа музыки,для Netscap'а , #Когда мне не будет так в облом это делать....... }
Ну вот вы уже можете управлять этим процессом.Только не забывайте,что если вы не получили информацию о клиенте(так может быть,если например ваш скрипт вызвал какая-нибудь поисковая машина) то не в этом случае не надо делать никаких предположений,а просто пусть ваш скрипт продолжает делать то что должен был делать.
Как всегда Примерчик на последок.Этот примерчик позволит выбирать из списка файлов. и загружать что пользователь хочет.
#!/usr/bin/perl #download.cgi
sub urldecode{ local($val)=@_; $val=~s/\+/ /g; $val=~s/%([0-9A-H]{2})/pack('C',hex($1))/ge; return $val; } @Filelist=qw(index.html readme.txt jmj00.mid gunshot.wav foto.gif); @Sel_list=(); if($ENV{'REQUEST_METHOD'} eq 'GET'){$query=$ENV{'QUERY_STRING'};} elsif($ENV{'REQUEST_METHOD'} eq 'POST'){sysread(STDIN,$query,$ENV{'CONTENT_LENGTH'});} if($query eq ''){ #Если никаких данных не подано на обработку,то сгенерируем форму, #которую и предложим заполнить пользователю. print "Content-Type: text/html\n\n"; print "<HTML><HEAD><TITLE>File Downloading</TITLE></HEAD>"; print "<BODY bgcolor=\"white\">"; print "Выберите файлы которые вы хотите загрузить:<BR>"; print "<FORM METHOD=\"POST\">"; print "<SELECT NAME=\"file\" size=4 multiple>"; foreach(@Filelist){ print "<OPTION value=\"$_\">$_"; } print "</SELECT><BR>"; print "<INPUT TYPE=\"Submit\" value=\"Download!\">"; print "</FORM>"; print "</BODY></HTML>" } else{ @formfields=split /&/,$query; foreach(@formfields){ if(/^file=(.*)/){push(@Sel_list,urldecode($1));} } unless(@Sel_list){ print "Content-Type: text/html\n\n"; print "<HTML><BODY><CENTER><H1>Вы должны выбрать что-то из списка"; print "</H1></CENTER></BODY></HTML>"; } else{ print "Content-Type: multipart/mixed;boundary=\"bhy3e23r4t34tnehtpo7678nneu4232y213vdg\"\n\n"; print "--bhy3e23r4t34tnehtpo7678nneu4232y213vdg\n"; foreach(@Sel_list){ print "Content-Type: application/x-qwerty; name=\"$_\"\n\n"; open F,"$_"; print <F>; close F; print "\n--bhy3e23r4t34tnehtpo7678nneu4232y213vdg\n"; } print "Content-Type: text/html\n\n"; print "<HTML><H1>Thats all folks!</H1></HTML>"; print "\n--bhy3e23r4t34tnehtpo7678nneu4232y213vdg--\n"; } }
Гостевая книга
А вот еще пример того,как можно с умом использовать нехитрые знания. Гостевая книга ,в которую каждый может записать свое вам пожелание.
К ней прилагаются .gif -файлы,позволяющие указать свое настроение:
Запись происходит в базу данных guestbook.dat и при каждой новой записи в гостевую книгу скрипт извещает по почте хозяина гостевой книги, а тому кто в нее вошел по почте посылается сообщение об этом.
#!/usr/bin/perl #guestbook.cgi $myemail="paaa\@uic.nnov.ru"; $myname="lesha"; $mail="mail"; ($sd,$sn)=($ENV{'SCRIPT_FILENAME'}=~/(.*)\/([^\/]*)/); $datafile=$sd."\/guestbook.dat"; @Mailgifs=qw(../gifs/mood0.gif ../gifs/mood1.gif ../gifs/mood2.gif); $Facetxt{$Mailgifs[0]}= ":)"; $Facetxt{$Mailgifs[1]}= ":|"; $Facetxt{$Mailgifs[2]}= ":(";
sub urldecode{ local($val)=@_; $val=~s/\+/ /g; $val=~s/%([0-9A-H]{2})/pack('C',hex($1))/eg; return $val; } sub strhtm{ local($val)=@_; $val=~s/&/&/g; $val=~s/</</g; $val=~s/>/>/g; $val=~s/(http:\/\/\S+)/<A href="$1">$1<\/A>/g; return $val; } $cont_len=$ENV{'CONTENT_LENGTH'}; if($ENV{'REQUEST_METHOD'} eq 'GET'){$query=$ENV{'QUERY_STRING'};} else {sysread(STDIN,$query,$cont_len);} if($query eq ''){ print "Content-type: text/html\n\n"; print <<HTML_generating; <HTML><HEAD><TITLE>Wellcome to my guestbook</TITLE></HEAD> <BODY bgcolor="cyan"> <CENTER><H1>Wellcome to my guestbook</H1></CENTER> <HR><FORM action="guestbook.cgi" METHOD="POST"> <table> <TR><TD>Name:</TD><TD colspan=3><INPUT NAME="Name"></TD></TR> <TR><TD>E-mail:</TD><TD colspan=3><INPUT NAME="Email"></TD></TR> <TR><TD>URL:</TD><TD colspan=3><INPUT NAME="URL"></TD></TR> <TR><TD>Message:</TD><TD colspan=3><TEXTAREA NAME="Message" rows=6 cols=64></TEXTAREA></TD></TR> <TR><TD>Mood:</TD><TD><IMG src="$Mailgifs[0]"></TD><TD><IMG src="$Mailgifs[1]"></TD><TD><IMG src="$Mailgifs[2]"></TD></TR> <TR><TD> </TD><TD><INPUT TYPE="radio" NAME="Mood" VALUE="$Mailgifs[0]"></TD> <TD><INPUT TYPE="radio" NAME="Mood" VALUE="$Mailgifs[1]"></TD> <TD><INPUT TYPE="radio" NAME="Mood" VALUE="$Mailgifs[2]"></TD></TR> <TR><TD colspan=2><INPUT TYPE="submit" VALUE="Send"></TD> <TD colspan=2><INPUT TYPE="reset" VALUE="Clean"></TD></TR> </TABLE></FORM> <HR><BR> HTML_generating open DATAFILE,"$datafile" die "Cannot open $datafile $!\n"; @GUESTDATA=<DATAFILE>; print @GUESTDATA; close DATAFILE; print "</BODY></HTML>"; } else{ foreach(@fields=split(/&/,$query)){ if(/^Name=(.*)/){$Name=&urldecode($1);} if(/^Email=(.*)/){$Email=&urldecode($1);} if(/^URL=(.*)/){$URL=&urldecode($1);} if(/^Message=(.*)/){$Message=&urldecode($1);} if(/^Mood=(.*)/){$Mood=&urldecode($1);} } $MESSAGE=&strhtm($Message); if(-e $datafile){unless (-r $datafile && -w $datafile){die "Cannot access $datafile\n";}} $Newmsg="<IMG src=\"$Mood\"><BR><A href =\"mailto:$Email\">$Name</A>". "(<A href=\"$URL\">$URL</A>):<BR>\n$MESSAGE<HR>\n"; open DATAFILE,"+<$datafile" die "Cannot open $datafile $!\n"; @GUESTDATA=<DATAFILE>; @GUESTDATA=($Newmsg,@GUESTDATA); seek(DATAFILE,0,0); print DATAFILE @GUESTDATA; close DATAFILE; print "Content-type: text/html\n\n"; print "<HTML><HEAD><TITLE>Congratulations</TITLE></HEAD>\n"; print "<BODY bgcolor=\"cyan\">\n<CENTER><H1>Congratulations:you have successfully entered to $myname\'s"; print "guestbook.Thank you!</H1></CENTER><HR>$Newmsg</BODY></HTML>"; open MAIL,"|$mail $Email"; print MAIL "Guestbook\n"; print MAIL "You have entered to $myname\'s guestbook\n"; print MAIL "Thank you.\n\t\t\t\t$myname"; close MAIL; format NOTIFYMAIL= Guestbook ========================== Guestbook Entry ======================= | Time: |Name: | | @<<<<<<<<<<<<<<<<<<<<<<<<|@<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< | scalar localtime,$Name +--------------------------+-------------------------------------+ | Email: |URL: | | @<<<<<<<<<<<<<<<<<<<<<<<<|@<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< | $Email,$URL +--------------------------+-------------------------------------+ | ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< | $Message | ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< | $Message | ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< | $Message | ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<< | $Message,$Facetxt{$Mood} ================================================================== . open NOTIFYMAIL,"|$mail $myemail"; write NOTIFYMAIL; close NOTIFYMAIL; }
PERL - статьи
Функция grep так-же позволяет запонять массив значениями. Например нужно получить список расширений файлов в заданной директории:
while(){push @files, $_} #читаем директорию @test = grep { s|.*/(.*?)\.(.*)|$2| } @files; #оставляем в директории только расширения файлов
можно использовать признак четности для занесения в массив: @test1=qw(1 2 3 4 5 6 7 8 9); @evens = grep($_%2 == 1) @test1;
Или более сложное регулярное выражение для вытаскивания всех e-mail адресов из текстовой странички:
@mass=grep{s/(.*) ([\w+\-\.]+\@[\w\-\.]+\.\w{2,3})(.*)/$2/ig} split /\n/, $test;
Здесь используется укороченная запись:
@mass=grep {/pattern/} split /\n/, $test;
которая эквивалента записи из двух сторчек:
@uuu=split /\n/, $test; @mass=grep {/pattern/} @uuu;
Хеши
Хэш - ассоциативный массив, т.к. доступ к данным осуществляется при помощи ключа, ассоциированного со значением.
Хеши хешей
Хеши, составленные из других хэшей, полезны при работе с текстовой многоуровневой
информационной системой (например, экспертной системой). В этих случаях
текстовые ключи используются для последовательного просмотра различных
уровней структуры данных. В следующем примере такой хэш создается за раз:
%hash = (
fruits => {
favorite => "apples",
'second favorite' => "oranges"
},
vegetables => {
favorite => "corn",
'second favorite' => "peas",
'last favorite' => "turnip"
},
meat => {
favorite => "chiken",
'second favorite' => "beef"
}
);
print $hahs{fruits}{favorite};
Обратите внимание, что в таком хэше значениями для пар ключ/значение
высупают другие хэши(точнее, ссылки на них).
Кроме того, для конструкций типа {...}{...},
между парами фигурных скобок неявно подставляется оператор-стрелка
-> - разыменования ссылок,
Создание хэша хэшей на лету:
Чтобы создать хэш хэшей элемент за элементом, используется та же схема, что и
в случае массива массивов, но с одним существенным отличием к хэшу всегда
можно добавить новые ключ и значение, но добавление к массиву новых элементов
с пропусками индексов порождает неопределенные значения. Пример:
%hash{fruits} = {favorite => "apples",
'second favorite' => "oranges"};
%hash{vegetables} = {favorite => "corn",
'second favorite' => "peas",
'least favorite' => "turnip"};
%hash{meat} = {favorite => "chicken",
'second favorite' => "beef"};
print $hash{fruits}{favorite};
В следующей схеме генератор анонимного хэша комбинируется со списком ключ/значение,
возвращаемым внешней подпрограммой:
for $key ("hash1", "hash2", "hash3")
{ $hash{$key} = {$returnlist}; }
sub returnlist{
return (key1 => valuel, key2 => value2);
}
print $hash{hash1}{key2};
Доступ к элеметнам хеша хешей
Чтобы получить отдельное значение, хранящееся в хэше хэшей, надо явно указать набор
последовательных ключей:
%hash = (
fruits => {favorite => "apples",
'second favorite' => "oranges"},
vegetables => {favorite => "corn",
"second favorite' => "peas",
"least favorite' => "turnip"}
);
print $hash{fruits}{"second favorite'};
Используя стандартные приемы работы с обычными хэшами, можно
организовать цикл по элементам хэша хэшей:
%hash = (
fruits => {favorite => "apples",
'second favorite' => "oranges"},
vegetables => {favorite => "corn",
"second favorite' => "peas"}
);
for $food (keys %hash) {
print "$food:\n\t {";
for $key (keys %{$hash{$food}}) {
print "'$key1' => \"$hash{$food}{$key}\",";
}
print "}\n";
}
Чтобы сортировать записи хэш-таблицы по ключам, в заголовок цикла можно
включить операцию сортировки:
%hash = (
fruits => {favorite => "apples",
second => "oranges"},
vegetables => {favorite => "corn",
second => "peas"}
);
for $food (keys %hash) {
print "$food:\n\t {";
for $key (keys %{$hash{$food}}) {
print "'$key1' => \"$hash{$food}{$key}\",";
}
print "}\n";
}
Хэш хешей массивов
Допустим в информации есть двойная степень вложенности, т.е. допустим 20 строк air и в этих 20 строках есть подкатегории по номерам, т.е. 20 строк состоят из трехподгрупп air,mumbra, air,kukumbra и air,telepuzikoff(on):
air,Acmar,one
air,Acmar,two
air,Airwell,one
air,Airwell,two
air,Ariston,one
air,Ariston,two
air,Ariston,three
air,Ariston,four
air,Mumbra,one
air,Mumbra,two
fridg,Ardor,one
fridg,Ardor,two
fridg,Ardor,three
fridg,Ardo-Young,one
fridg,Ardo-Young,two
fridg,Ardo-Young,three
wei,Tefal,one
wei,Tefal,two
wei,Tefal,three
Структурировать такую информацию можно при помощи гибрида хеша хешей и хеша массивов, т.е. хеша хешей массивов:
#!/usr/bin/perl
open F, "<example" or die "can open: $!\n"; @mass=<F>; close F;
for $gr(grep{!$_{$_}++} map{/^(.*?),/} @mass){
for $line(@mass){push @{$hash{$gr}{$1}}, $2 if $line=~m!^$gr,(.*?),(.*)!}
}
print "name hash\tkeys\t\tmassives\n\n";
for $a(sort keys %hash){
print "hash $a: \n\t{\n\t";
for $key(sort keys %{$hash{$a}}){
print "'$key' \t => [ ";
print join " | " => @{$hash{$a}{$key}};
print " ]\n\t";
}
print "}\n";
}
на выходе такая программа выдаст:
name hash keys massives
hash air:
{
'Acmar' => [ one | two ]
'Airwell' => [ one | two ]
'Ariston' => [ one | two | three | four ]
'Mumbra' => [ one | two ]
}
hash fridg:
{
'Ardo-Young' => [ one | two | three ]
'Ardor' => [ one | two | three ]
}
hash wei:
{
'Tefal' => [ one | two | three ]
}
Доступ к определенному элементу массива можно получить, указывая явно набор ключей ${$hash{$a}{$key}}[$n], где $n < $#{$hash{$a}{$key}}
Хэши массивов
Хэши, состоящие из массивов, позволяют разбивать данные, индексированные
числовым значением, на записи. В следующем примере мы объявляем хэш массивов в одном предложении:
%hasn = (
fruits => ["apples", "oranges"],
vegetables => ["corn", "peas", "turnips"],
meat => ["chicken", "ham"],
);
print $hash{fruits}[0];
Обратите внимание, что для конструкций вида [...]{...},
как и для рассматриваемых ранее конструкций
вида {...}{...} и
[ ][[ ], между парами скобок неявно
подставляется оператор-стрелка -> разыменования ссылок.
Создание хэша массивов на лету
Чтобы собрать хэш массивов из отдельных элементов, можно заносить в хэш под нужным
ключом ссылки на массивы, созданные генератором анонимных массивов:
%hash{fruits} = ["apples", "oranges"];
%hash{vegetables} = ["corn", "peas", "turnips"];
%hash{meat} = ["chicken", "ham"];
print $hash{fruits}[0];
Если вы предпочитаете другой вариант, можете воспользоваться функцией push
и контекстно-чувствительной процедурой автооживления ссылок (autovivification).
push @{%hash{fruits}}, "apples", "oranges";
push @{%hash{vegetables}}, "corn", "peas", "turnips";
push @{%hash{meat}}, "chicken", "ham";
Доступ к элементам хэша массивов
Вы всегда можете получить доступ к отдельному элементу данных, хранимому в
хэше массивов, указав ключ и индекс:
%hasn = (
fruits => ["apples", "oranges"],
vegetables => ["corn", "peas", "turnips"],
meat => ["chicken", "ham"],
);
print $hash{fruits}[0];
В следующем примере мы полностью выводим отсортированный по значениям
ключа хэш массивов, используя функцию
join для преобразования массивов в текстовые строки:
%hasn = (
fruits => ["apples", "oranges"],
vegetables => ["corn", "peas", "turnips"],
meat => ["chicken", "ham"],
);
for $key(sort keys %hash){
print "$key\t[", join(", ", @{$hash{$key}}), "]\n";
}
Хэши slice
(перевод, статья находится по адресу )
Насколько хорошим является этот код??
@array = qw( a b c d ) ;
@array{ @array } = ( [ @array ] ) x @array ;
Основное определение хэша slice: @hash{ @array } = ( list ) что эквивалентно ( $hash{ $array[0] }, ... $hash{ $array[$#array] } ) = ( list ) Префиксный символ (@ ,%) используется только для обозначения контекста, но не типа данных. Если имеются два массива и мы ходим сопоставлять их данные, то мы поступаем так:
@foo_array = qw( abc def ghi ) ;
@bar_array = qw( jkl mno pqr ) ;
@foo_to_bar{ @foo_array } = @bar_array
теперь можно работать с массивами немного проще:
$foo_value = 'def' ;
$bar_value = $foo_to_bar{ $foo_value } ;
Можно даже сразу преобразовывать целый массив переменных foo одной конструкцией: @bar_values = @foo_to_bar{ @foo_values }; Еще одно использование хэша slice, производится проверка: находится ли строка в данном массиве строк? Фактически мы не заботимся о том, что находится в хеше slice, но 1 используется для большей ясности.
@foo_array = qw( abc def ghi ) ;
@is_a_foo{ @foo_array } = (1) x @foo_array ;
$input = 'def' ;
if ( $is_a_foo{ $input } ) {
...
if ( exists( $is_a_foo{ $input } ) ) {
...
Оператор x называется оператором повторения, он дублирует левый операнд значением правого операнда. В скалярном контексте он копирует левый оперант как строку и возвращает его, т.е. мы имеем контекст списка и левый операнд списка. Это позволяет создать новый список с N копиями предыдущего списка. Для foo N = 3 и (скалярное значение @foo_array) и мы получаем список (1, 1, 1) который присваивается хешу slice. Программа, преобразующая строки в нумерованные индексные значения. Используется оператор диапазона (..), создающий список целых чисел, которые присваиваются хэшу slise:
@foo_array = qw( abc def ghi ) ;
@foo_to_index{ @foo_array } = ( 0 .. $#foo_array ) ;
@foo_to_index{ @foo_array } = ( 1 .. @foo_array ) ;
$i_am_a_foo = 'def' ;
$foo_index = $foo_to_index{ $i_am_a_foo } ;
Названия %foo_to_bar, %is_a_foo, $foo_to_index подобраны так, чтобы нагляднее показать принцип работы slise. Фактически была проведена индексация входных данных и выходных данных в хеш splice. Теперь рассмотрим хэш splice снова @array{ @array } = ( [ @array ] ) x @array ;. Заметим, что хэш и массив(и скаляр) называются массивами, но это различные переменные, в частности отличные по разделитеям. Левая часть выражения - присваивание хэшу slice, но что-же стоит справа? Оператор x и @array справа дублирует повторения счетчика и некоторый лист слева и мы имеем контекстный список. Хеш slice есть список контекстов, мы создаем скопированный список анонимного массива, который содержит значения в @array. Это значит что хэш %array напоминает такой массив:
%array = (
'a' => [ 'a', 'b', 'c', 'd' ],
'b' => [ 'a', 'b', 'c', 'd' ],
'c' => [ 'a', 'b', 'c', 'd' ],
'd' => [ 'a', 'b', 'c', 'd' ],
) ;
Если имеется набор псевдонимов(alias), то можно, обращаясь к списку, вывести каждый из них в отдельности(ср. alias для сокращения часто употребляемой команды в unix). Т.е. вводится любой из отдельных элементов и мы получаем список данных. Если имеется список массивов, то их можно обработать таким образом:
@foo_list = qw( a b c d );
@bar_list = qw( j k l m n o );
@baz_list = qw( w x );
@expand_aliases{ @foo_list } = ( [ @foo_list ] ) x @foo_list;
@expand_aliases{ @bar_list } = ( [ @bar_list ] ) x @bar_list;
@expand_aliases{ @baz_list } = ( [ @baz_list ] ) x @baz_list;
Если есть лексемма неопределенного типа, то можно получить список псевдонимов за 1 шаг @aliases = @{ $expand_aliases{ $alias } } ; Окружение @{} используется, чтобы разыменовать сохраненный анонимный список в список для присвоения @aliases.
есть хэш, типа
$myhash{name}=qwert;
$myhash{age}=15;
как сделать автоматическое создание переменной=ключу хэша со значением
из этого хэша с этим же ключом, т.е. $name=qwert; $age=15 и т.д.
либо так:
my %myhash;
$myhash{name}='qwert';
$myhash{age}=15;
for $abc (keys %myhash)
{
$$abc=$myhash{$abc};
}
print "\$name=$name; \$age=$age\n и т.д.";
либо так:
$$_ = $myhash{$_} for keys %myhash;
что в принципе одно и то-же...
from:
Объявление анонимного хеша:
Ссылки на существующие данные часто применяются для передачи аргументов функции, но в динамическом программировании они бывают неудобны. Для любой переменной
есть два параметра, е имя и адрес области памяти, где хранится эта переменная. Скалярная величина, хранящая адрес области памяти, назвается ссылкой. Значение, хранящееся в памяти по данному адресу называется субъектом(referent).
Анонимные хеши в perl могут создаваться явно. При этом выделяется память для хеша и возвращается ссылка на не .
$hash{map{$_ => $_*$_}(0 .. 4)}
while(($k, $v) = each %$hash){
print "$k => $v\n";
}
print $hash->{"3"}, "\n";
в цикле использовалась ссылка $hash на анонимный хеш.
Обратиться к нужному элементу хеша можно и при помощи ассоциативного оператора стрелка ->:
print $hash->{"3"}, "\n";
Пример использования анонимного хеша для выкидывания повторяющихся элементов из массива:
keys %{{ map { $_, 1 } @array }} # @array -> anonymous hash -> keys
Источник:
Использование встроенных переменных
$' - подстрока, следующая за совпадением.
$& - совпадение с шаблоном поиска
$` - подстрока, расположенная перед совпадением
$^R - результат вычисления утверждения в теле шаблона
$n - n-ный фрагмент совпадения
\n - n-ный фрагмент совпадения вызываемый в самом шаблоне
$+ - фрагмент совпадения
$* - разрешает выполнять поиск в многострочных файлах
@- - спецмассив, который содержит начальную позицию найденного слова
@+ - массив, содержащий позицю последнего найденного слова
$& - совпадение с шаблоном поиска, при последней операции поиска или замены. В отличии от переменной $_, эту переменную переопределять как вздумается нельзя.
$' подстрока за совпадением с шаблоном поиска, е также можно только читать.
$` - подстрока, расположенная перед совпадением, разрешается только е чтение.
$^R - результат вычисления утверждения в теле шаблона для последнего вычисления шаблона, если в нем идет счет или вызывается внешняя программа:
$qwer="lala"; $qwer=~ /x(?{$var=5})/; print $^R; 5
$+ - фрагмент совпадения в шаблоне, который в нем был последним в круглых скобках. Разрешается только чтение $+.
$* - разрешает выполнять поиск в многострочных файлах, булева переменная, если она взведена в 1, то символы шаблона поиска ^ и $ сопоставляются позициям перед и после внутренних символов новой строки, если 0, то от начала текста и до конца текста:
$kim="lala\nfa\eti\nzvuki..."; $kim=~~ /^eti/; #совпадение не нашлось $*=1; $kim=~~ /^eti/; #совпадение нашлось
$n - n-ный фрагмент совпадения:
print "$1 $2 $3\n" if(/^(\d)(\w)(\W)$/);
\n - n-ный фрагмент совпадения вызываемый в самом шаблоне, например поиск гиперссылок:
/a href=(['"])(.*?)\1>/
Например нужно занести в массив только цифры из строчки "12@#34@@#@@###34@@##67##@@#@#@34":
$_='12@#34@@#@@###34@@##67##@@#@#@34'; s/@/#/g; s/(#)\1+/$1/g; print join /\n/, split /#/, $_;
Регулярное выражение s/(#)\1+/$1/g; изпользует повторение переменной $1 (квантификатор +) и если оно есть, то заменяет все подряд идущие # между цифрами на одну #, содержащуюся в$1(переменная $1 существует, если часть шаблона или шаблон указать в круглых скобках).
Допустим нужно определить, все ли цифры числа различны. Попробуем найти хотя-бы одно повторяющееся число:
if(/(\d).*(?=\1)/g){ print "по крайней мере одна цифра $1 различна\n"; }
Выражение берет 1-ю цифру и ищет е совпадения со всеми остальными, если есть, то говорит, что найдено и заканчивает работу. Регулярное выражение берет первое число при помощи (\d) и начинает его сравнивать со всеми остальными числами при помощи .*(?=\1). Если первое число в строке уникально, регулярное выражение начнет сопостовлять второе число со всеми восемью оставшимися числами. Если и второе число в строке уникально, то берется третье число и сравнивается со всеми остальными. И т.д., если совпадение было найдено, то регулярное выражение возвращает true и заканчивает свою работу, даже если в строке еще есть повторяющиеся числа. Чтобы можно было просмотреть все повторяющиеся числа, можно воспользоваться модификацией предыдущего кода:
$_ = '2314152467'; my @a = m/(\d)(?=\d*\1)/g ; if (@a){ print join(',',@a)," - Repeat\n"; } else{ print "Ok\n" ; }
Этот усовершенствованный код работает до тех пор, пока не будут найдены все совпадения, если таковые вообще есть.
В perl 5.6 вводятся переменные @- и @+, комбинация которых может заменять переменные $`, $&, и $'. После совпадения шаблона переменная $-[0] содержит начало соответсвия текста шаблону, а переменная $+[0] содержит конец соответсвия текста шаблону. В начале поиска обе являются нулями. Это значит, что можно вычислить значения $`, $&, и $':
$do = substr($stroka, 0, $-[0]); $sovpalo = substr($stroka, $-[0], $+[0] - $-[0]); $posle = substr($stroka, $+[0]);
Например:
$test="11-231234"; $test=~/\d{2}-\d{6}/; print "$-[0], $+[0]"; 0, 9
Соответственное переменные $#- и $#- указывают размерность массивов @- и @+.
Переменная $^N.
Итак ...приступим...
Краткие сведения о том что надо знать чтоб писать CGI скрипты: Ну вопервых надо знать что такое интернет
и как он работает (а вы знаете? ;)))
) Ну и чуть-чуть умения прграмировать(это самое главное)
Давайте вместе писанем какой нибудь простенький скриптик а потом я вам расскажу где сдесь собака порылась....
Ну сначала в своем домашнем каталоге создайте директорию cgi-bin:
cd public_html
mkdir cgi-bin
chmod 0777 cgi-bin
Последняя строчка будет очень важна.
Возьмите редактор и наберите:
#!/usr/bin/perl #first.cgi print "Content-Type: text/html\n\n"; print "<HTML><BODY>"; print "<H1>Hello you!!!</H1>"; print "</BODY></HTML>";
Сохраните его в директории cgi-bin
под именем first.cgi .Ну как сохранили?
А теперь сделайте его исполняемым(ведь это программа):
chmod +x first.cgi
Ну вот,подходим к торжественному моменту.... наберите в строке браузера http://www.uic.nnov.ru/~твой_логин/cgi-bin/first.cgi
и посмотрите чо будет. Будет одно из двух ,либо скрипт заработает и вы увидите сгенерированую им страничку (поздравляю,в нашем полку прибыло!) либо Internal Server Error
-тогда не расстраивайтесь,вы что-то сделали не так. Вам тогда пригодится пособие по ловле блох. Ну вопервых проверку синтаксиса можно осуществить следующим образом:
perl -с first.cgi
Perl вам сразу выдаст либо сообщения об ошибках(ну бывает,точку с запятой пропустили, скобочки или кавычки забыли закрыть...) это сразу по ходу дела поправимо.
Более грубая с логической точки зрения это пропустить вывод пустой строки, которая отделяет заголовок от тела:
print "Content-Type: text/html\n\n"; #Все Правильно print "Content-Type: text/html\n"; #ОШИБКА!!!
Разберем скрипт:
Первая строка #!/usr/bin/perl Просто указывает где в системе расположен Perl. Вторая это просто коментарий -вы можете тыкать чо угодно после знака #
Затем идет print "Content-Type: text/html\n\n"; Это заголовок указывающий тип содержимого все что скрипт печатает в свой стандартный вывод STDOUT идет на обработку к серверу. Пустая строка отделяет заголовок от тела,которое в нашем случае представляет собой
<HTML><BODY>
<H1>Hello you!!!</H1>
</BODY></HTML>
Сервер обработает ответ скрипта и на базе него сформирует и пошлет браузеру ответ.(Сервер обычно не изменяет тела сообщения,он только дополняет заголовок нужными для протокола HTTP полями)
Ну вот азы уже освоены,все не так трудно и удручающе как могло показаться на первый раз
Вы теперь можете сами потренироваться в написании таких вот простеньких скриптов чтоб набить руку.
Изображения ismap
После такой серьезной темы как обработка форм,я перейду к чему-нибудь веселенькому. Я познакомлю вас с изображениями ismap потому что это просто есть такой способ. Он поддерживается браузерами и естественно имеет право на жизнь.Хотя с приходом новых веяний в HTML (особенно Java-аплетов) он стал настоящей редкостью.И хотя можно в 80% случаев найти ему более быструю замену,все-же вы можете в некоторых случаях найти именно ismap предпочтительней всего.
Синтаксис очень простой,почти не отличается от того,если бы вы решили оформить рисунок для якорь гиперссылки:
<A href="cgi-bin/somescript.cgi"><IMG src="somepic.gif" border=0 ismap></A>
Заметьте что все отличие заключается в том,что в тэге IMG добавлен атрибут ismap. Он говорит браузеру,что когда пользователь щелкнет на картинке то нужно перейти не просто к URL указаному в <A href="URL"> а что нужно к этому URL добавить координаты
той точки по которой пользователь щелкнул мышью .
В нашем примере если пользователь щелкнул по точке x=10 ,y=15 то браузер перейдет на URL:
http://www.somehost.ru/cgi-bin/somescript.cgi?10,15
Т.е. координаты идут на скрипте в переменную QUERY_STRING ,их оттуда извлечь? Нет ничего проще:
($x,$y)=split /,/,$ENV{'QUERY_STRING'};
Вот скрипт,который просто показывает координаты точки щелчка:
#!/usr/bin/perl #ismap_xy.cgi ($x,$y)=split /,/,$ENV{'QUERY_STRING'}; print "Content-Type: text/html\n\n"; print "<HTML><HEAD><TITLE>Ismap X Y</TITLE></HEAD>"; print "<BODY><H1>Вы щелкнули в точке: x=$x ,y=$y</H1></BODY></HTML>";
А что с ними делать дальше это уже чисто зависит только от вашей фантазии.Дайте ей ход и все у вас получится!.Очень часто ismap применяют для графического оглавления сайта. Когда щелкают на разные части рисунка,то переходят к разным страничкам сайта. Это легко реализуется,если скрипт выдаст нужный URL в Location:
(Вспомните ).
Вот пример и покажет это.Заготовьте файл urlmap.txt в котором будет информация из строк в таком формате:
minx miny maxx maxy URL
где minx miny maxx maxy задают участок рисунка,а следующее за ними поле задает URL, которому этот участок соответствует.Пример:
1 1 20 50 http://www.uic.nnov.ru/~paaa/index_p.html 1 50 20 100 http://www.uic.nnov.ru/~paaa/projects.html 20 1 100 100 http://www.uic.nnov.ru/~paaa/cgi-bin/guestbook.cgi
Где нибудь на своей страничке воткните что-то вроде:
<A href="cgi-bin/testismap.cgi"><IMG src="gifs/doom2.jpg" border=0 ismap></A>
А сам скрипт testismap.cgi будет иметь вот такой простенький вид:
#!/usr/bin/perl #testismap.cgi $default_url="http://www.uic.nnov.ru/~paaa/";#URL по умолчанию,переходим к нему когда щелкнули #в участок,которому не сопоставлен URL $url_map_file="urlmap.txt"; #файл с информацией об URL
($x,$y)=split /,/,$ENV{'QUERY_STRING'}; open F,"$url_map_file" print "Location: $default_url\n\n"; $url=$default_url; foreach(<F>){ chomp; ($minx,$miny,$maxx,$maxy,$URL)=split /\s+/; if(($x>=$minx)&&($x<$maxx)&& ($y>=$miny)&&($x<$maxy)){$url=$URL;} } close F; print "Location: $url\n\n";
Как работают регулярные выражения
Регулярные выражения, использующие квантификаторы, могут порождать процесс, который называется перебор с возвратом (backtracking). Чтобы произошло совпадение текста с шаблоном, надо построить соответствие между текстом и всем регулярным выражением, а не его частью. Начало шаблона может содержать квантификатор, который поначалу срабатывает, но впоследствии приводит к тому, что для части шаблона не хватает текста или возникает несоответствие между текстом и шаблоном. В таких случаях perl возвращается назад и начинает построение соответствия между текстом и шаблоном с самого начала, ограничивая "жадность" квантификатора (именно поэтому процесс и называется "перебор с возвратом"). Перечислим квантификаторы perl:
* - ноль или несколько совпадений,
+ - одно или несколько совпадений,
? - ноль совпадений или одно совпадение,
{n} - ровно n совпадений,
{n,} - по крайней мере n совпадений,
{n,m} - от n до m совпадений.
Например квантификатор + соответствует фразе "один или несколько" и является жадным. Расмотрим пошагово принцип перебора с возвратом на примере квантификатора +:
'aaabc' =~/a+abc/;
a+ сразу в силу жадности совпадает с тремя а:
(aaa)bc
но после aaa не следует строка "abc", а следует "bc". Поэтому результат - failed поэтому анализатор должен откатиться назад и вернуть с помощью a+ два a:
(aa)abc
т.е. на втором шаге шаблон найдет совпадение.
Рассмотрим пример работы еще одного жадного квантификатора *(ноль или несколько совпадений):
amxdemxg /.*m/
Сначала будет найдена вся строка abcdebfg в силу жадности .*, потом квантификатору нужно будет найти сравнение с буквой m, произойдет ошибка. Квантификатор .* отдаст одну букву и его содержимое будет уже amxdemx. На конце снова нет буквы m. Будет отдана еще одна буква и снова не будет найдено совпадение со всем шаблоном и наконец квантификатор .* будет содержать подстроку amxde, за которой уже стоит символ m. И поиск на этом и закончится не смотря на то, что в строке amxdemxg содержится не одна буква m. Потому и говорят, что квантификаторы обладают жадностью, т.е. находят максимально возможное совпадение.
Допустим нужно найти совпадение:
$uu="How are you? Thanks! I'm fine, you are ok??"; $uu=~s/.*you//; print $uu;
Квантификатор .* оставит текст " are ok??", а вовсе не "? Thanks! I'm fine, you are ok??". Если же поставить ограничитель ?, который вместе со знаком квантификатора означает максимально возможное совпадение
$uu="How are you? Thanks! I'm fine, you are ok??"; $uu=~s/.*you//; print $uu;
то переменная $uu будет содержать текст "? Thanks! I'm fine, you are ok??".
Предположим нужно найти совпадения типа network workshop, т.е. перекрытия.
$u='network'; $m='workshop'; print "перекрытие $2 найдено: $1$2$3\n" if("$u $m" =~/^(\w+)(\w+) \2(\w+)$/);
$1 сразу берет все слово в $u, но дальше идет еще один максимальный квантификатор (\w+), которому тоже чего-то надо и он забирает из переменной \1 букву k(причем только одну):
#!/usr/bin/perl $uu="asdfg asdf"; $uu=/(\w+)(\w+)\s(\w+)(\w+)/; print "$1 $2##$3 $4"; asdf g##asd f
далее пошаговая работа regex выглядит примерно так:
1: 'networ''k'=> '\sk' совпадает ли с '\sworkshop' falure 2: 'netwo''rk'=> '\srk' совпадает ли с '\sworkshop' falure 3: 'netw''ork'=> '\sork' совпадает ли с '\sworkshop' falure 4: 'net''work'=> '\swork' совпадает ли с '\sworkshop' ok
и в результате программа выдаст: перекрытие work найдено: networkshop
Данный регексп не сработает, если
$u='networkwork'; $m='workshop';
шаблон найдет перекрытия workwork, а не work. Чтобы этого избежать, нужно сделать минимальным \1: /^(\w+?)(\w+) \2(\w+)$/
Квантификатор действует только на предшествующий ему элемент шаблона. Например, конструкция \d{2}[a-z]+ будет соответствовать последовательности из одной или нескольких строчных латинских букв, начинающейся с двух цифр, а не последовательности, составленной из чередующихся цифр и букв. Для выделения группы элементов, на которую действует квантификатор, используются круглые скобки: (\d{2}(a-z])+
Какие броузеры поддерживают механизм cookies?
Не все, конечно, однако самые популярные поддерживают. Я точно
знаю, что Netscape (начиная с самой первой версии), Microsoft
IE (трешка и четверка - точно, про предыдущие не знаю), Mosaic
Классы символов
Символы могут быть сгруппированы в классы. Указанный в шаблоне класс символов сопоставляется с любым из символов, входящим в этот класс. Класс - это совокупность символов, заключенный в квадратные скобки [ и ]. Можно указывать как отдельные символы, так и их диапазон (диапазон задается двумя крайними символами, соединенными тире). Наример, следующий код производит поиск гласных: $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.
Книжки
Q: Какую книгу для начинающих посоветуете?
A: Llama Book(книжка с Ламой на обложке), она же "Изучаем перл" в русском издании. С ламой на обложке.
Q: А для продолжающих?
A: Perl Cookbook. Переведенена как "Perl. Библиотека программиста". Список
конкретных задач и их решений, плюс уроки хорошего стиля программирования.
Q: А в электронном виде где книги взять?
A: Llama Book по-русски: ftp://molly.wbt.ru/incoming/lama.rar
http://ag.ru/files/llama.exe
Есть такой сборник Perl Bookshelf - лучшие книги по перл издательства
O'Reilly. Hа английском, разумеется. Лежит на:
http://lpcs.math.msu.ru/CD_Bookshelf/Perl/
В виде архива ее можно взять как
ftp://twilight.telco.md/pub/books/perlbookshelf.zip
http://ag.ru/files/perlbookshelf.zip
ftp://freebsd.svib.ru:21/incoming/ora/perlbookshelf.zip
(дополнительные ссылки принимаются).
Perl Cookbook в HTML-виде из этого комплекта, но с исправленными опечатками
(список опечаток - на www.oreily.com):
http://meltingpot.fortunecity.com/nj/80/cookbook.rar
Hеплохая, по слухам, книжка для начинающих: http://www.effectiveperl.com
The Perl Journal: www.itknowledge.com/tpj/
Заметки по Perl-у: http://www.perlreference.com
http://perlfect.com/articles/
http://www.perlmonth.com/
http://www.stonehenge.com/merlyn/WebTechniques/
http://www.stonehenge.com/merlyn/UnixReview/
http://www.stonehenge.com/merlyn/LinuxMag/
http://www.plover.com/~mjd/perl/#tpj
http://home.od.ua/watman/perl/
http://spider.softclub.net/books/web/
http://athena.vvsu.ru/docs/unix/
http://athena.vvsu.ru/docs/tcpip/
http://athena.vvsu.ru/docs/www/
http://webims.virtualave.net/docs/
http://www.citforum.ru
http://home.od.ua/watman/perl/
Краткое лирическое отступление насчет CGI
Итак что такое CGI- скрипты и вообще подобные вещи. Начнем с того что ваш браузер (когда вы набрали URL) соединяется по протоколу HTTP с указаным сервером и просит у него нужный файл,примерно так:
GET /~paaa/cgi-bin/guestbbok.cgi HTTP/1.0 -Вот это самое главное в запросе
Ну тут дальше идет посылаемая браузером информация о себе и о том что более подробно ему надо.(Например Accept: */*)
Ну и если запрошен простой файл например .html то если если такой файл есть, То сервер отошлет браузеру ответ:
HTTP/1.0 200 Okay
Content-Type: text/html
Далее после пустой строки(она нужна чтоб отделить заголовок от тела) идет информация из самого URL'а <HTML><BODY>...
Вот в принципе и весь WWW
....ходишь от ссылки к ссылке....
А что если Нужно внести в этот унылый процесс что-нибудь по настоящему интерактивное , динамическое,прекрасное и великолепное....? Чтож есть ответ и на этот вопрос. Просто что если в запрашиваемом URL указать спецыальную программу (CGI,программа Common Gateway Inteface - Общего Шлюзового Интерфейса) и то что эта прога выдаст то и отправить браузеру....Сервер запускает .cgi
программу и она например обработав данные формы заносит вас куда-нибудь в свою базу данных,а вам сообщит что вы большой молодец :)
Ну надеюсь я вас заинтриговал......?
Кто посещает мою страничку?
Вам иногда хотелось наверное узнать,кто же смотрит на вашу страничку,откуда и когда ваша страничка посещалась.
Бывают такие вопросы? Кто-то считает,что ответить на них нельзя. Но вы не верьте этому расхожему мнению.
Один раз с подобными вопросами ко мне подошел мой одногрупник, Диман. У него неплохой сайт. и туда к нему всегда валит целая куча народа. Вот как раз разговор и зашел об этой куче народа. Результатом моего непродолжительного труда стал небольшой скрипт.В страничку он втакается через тэг <IMG src="cgi-bin/get_ip.cgi"> он покажет вам изображение что не будет бросаться в глаза. Зато при своей работе он все запишет в файл ipdata.txt : В нем будет время и IP-адрес того,кто смотрел на вашу страничку!
#!/usr/bin/perl #get_ip.cgi $gif="../gifs/player.gif"; $data="ipdata.txt"; print "Content-Type: image/gif\n\n"; open G,$gif; print <G>; close G; open D,">>$data"; print D scalar localtime,' '.$ENV{'REMOTE_ADDR'}."\n"; close D;
Квантификаторы
Квантификаторы в регулярных выражениях
Квантификаторы указывают на то, что тот или иной шаблон в строке может повторяться определенное количество раз. Например, можно использовать квантификатор + для поиска мест неоднократного, повторения подряд латинской буквы е и их замены на одиночную букву е: $text = "Hello from Peeeeeeeeeeeeeeerl."; $text =~ s/e+/e/: print $text; Hello from perl.
Логические операции в регулярных выражениях
В регулярных выражениях perl есть синтаксические выражение, позволяющие в шаблонах использовать простые логические конструкции:
(?= шаблон) - после этой точки есть фрагмент текста, который соответствует указанному регулярному выражению
(?! шаблон) - после этой точки нет текста, который бы соответствовал указанному регулярному выражению,
(?<= шаблон) - перед этой точкой есть фрагмент текста, соответствующий указанному регулярному выражению,
(?<! шаблон) - перед этой точкой нет фрагмента текста, соответствующего указанному регулярному выражению.
(?#текст) - комментарий. Текст комментария игнорируется.
(?:шаблон) или (?модификаторы:шаблон) - группирует элементы шаблона. В отличие от обычных круглых скобок, не создает нумерованной переменной. Например, модификатор i не будет делать различия между строчными и заглавными буквами, однако область действия этого модификатора будет ограничена только указанным шаблоном.
(?=шаблон) - "заглядывание вперед". Требует, чтобы после текущей точки находился текст, соответствующий данному шаблону. Такая, конструкция обрабатывается как условие или мнимый символ, поскольку не включается В результат поиска. Например, поиск с помощью команды /w+(?=\s+)/ найдет слово, за которым следуют один или несколько "пробельных символов", однако сами они в результат не войдут.
(?!шаблон) - случай, противоположный предыдущему. После текущей точки не должно быть текста, соотносимого с заданным шаблоном. Так, если шаблон w+(?=\s) - это слово, за которым следует "пробельный символ", то шаблон w+(?!\s) - это слово, за которым мет "пробельного символа".
(?<=шаблон) - заглядывание назад. Требует, чтобы перед текущей точкой находился соответствующий текст. Так, шаблон (?<=\s)w+ интерпретируется как слово, перед которым имеется пробельный символ (в отличие от заглядывания вперед, заглядывание назад может работать только с фиксированным числом проверяемых символов).
(?<!шаблон) - отрицание предыдущего условия. Перед текущей точкой не должно быть текста, соотносимого с заданным шаблоном. Соответственно, от команды /(?<!\s)w+/ требуется найти слово, перед которым нет пробельного символа.
(?{код}) - условие (мнимый символ), которое всегда выполняется. Сводится к выполнению команд perl в фигурных скобках. Вы можете использовать эту конструкцию, только если в начале сценария указана команда use re 'eval'. При последовательном соотнесении текста и шаблона, когда perl доходит до такой конструкции, выполняется указанный код. Если полного соответствия для оставшихся элементов найти не удалось, то при возврате левее данной точки шаблона вычисления, проделанные с локальными переменными, откатываются назад. (Условие является экспериментальным. В документации, прилагаемой в perl, можно найти довольно детальное рассмотрение (с примерами) работы этого условия и возможных трудностей в случае его применения.)
(?>шаблон) - "независимый" или "автономный" шаблон. Используется для оптимизации процесса поиска, поскольку запрещает "поиск с возвратом". Такая конструкция соответствует подстроке, на которую налагается заданный шаблон, если его закрепить в текущей точке без учета последующих элементов шаблона. Например, шаблон (?>а*)аb в отличие от a*ab не может соответствовать никакой строке. Если поставить в любом месте шаблон а*, он съест все буквы а, не оставив ни одной шаблону ab. (Для шаблона а*аb "аппетит" квантификатор * будет ограничен за счет работы поиска с возвратами: после того как на первом этапе не удастся найти соответствие между шаблоном и текстом, perl сделает шаг назад и уменьшит количество букв а, захватываемых конструкцией а*.)
(?(условие)шаблон-да|шаблон-нет) или (?(условие)шаблон-да) - условный оператор, который подставляет тот или иной шаблон в зависимости от выполнения заданного условия. Более подробно описан в документации perl.
(?модификаторы) - задает модификаторы, которые локальным образом меняют работу процедуры поиска. В отличие от глобальных модификаторов, имеют силу только для текущего блока, то есть для ближайшей группы круглых скобок, охватывающих конструкцию, Например, шаблон ((?i)text) соответcтвует слову "text" без учета регистра.
Поиск повторяющихся слов в регулярном выражении осуществляется при помощи т.н. обратных ссылок. Выше уже был приведен пример их использования для выбирания всех адресов рисунков с www.astronomynow.com:
m{SRC\s*=\s*(["'])http://(.*?)\1\s+(.*?)WIDTH="100" HEIGHT="100"(.*?)>}igs
(["']) - найти либо " либо ' либо ничего, т.к. src=http:// может быть без кавычек. Как только был надено что-либо из этих трех позиций, через минимальное количество символов(регулярное выражение (.*?)) символов оно заносится в специальную переменную \1, которая вне m/.../ может быть вызвана как $1(в s/.../.../ она вызывается в его левую половину как $1). Дальше после *.gif|*.jpg|*.bmp и т.д. должен обязательно идти хотя-бы один пробел \s+, т.к. броузеры воспримут подстроку src=file.gifborder=0 как файл картинки с расширением gifborder=0. Поэтому данное регулярное выражение вполне исправно работает, хотя оно было сделано для сайта, где в img src ставится полный адрес, т.е. начинающийся с http:// Для других сайтов придется выстраивать полные пути в ссылках используя base href, если есть или его url.
Если нужно найти какое-то по счету совпадение шаблона в строке, то это реализуется примерно так:
while($str=~/WHAT/g){$n++} $n++ while $str=~/WHAT/g; $n++ while $str=~/(?=WHAT)/g;#для перекрывающихся совпадений for($n=0; $n=~/WHAT/g; $n++){}
Каждое кратное совпадение
(++$n % 6) == 0;
Нужное Вам совпадение:
$n=($str=~/WHAT/gi)[6]; #допустим шестое
Или каждое четное совпадение
@mass=grep{$n++ %2==0} /WHAT/gi;
для нечетного нужно написать внутри grep: $n++ %2==1
Логические операции внутри регулярных выражений.
Если нужно найти последнее совпадение, то можно воспользоваться отрицанием опережающей проверки (?!WHAT):
m#PATTERN(?!.*PATTERN)$#
т.е. нийти какой-то PATTERN, при этом не должно найтись что-то еще(.*) и PATTERN, т.е. результат - последнее совпадение;
Минимальные квантификаторы *?, +?, ??,{}?
допустим нужно найти двойку, перед которой не стоит 3 или пробел:
print "$1\n" while m%2(?![3\s])gm%;
используется условие по отрицанию, A(?!B): найти А, перед которым не находится В. Чтобы найти двойку, за которой стоит 3 или пробле(\s), то можно воспользоваться:
print "$1\n" while m%2(?=[3\s])gm%;
или
print "$1\n" while m%2(?![^3\s])gm%;
где используется ^, [^3\s], который значит следущее: в класс символов, которые нужно найти, не входят 3 и пробел, или другими словами найти все кроме 3 и \s.
Допустим существует HTML-документ, в котором произвольное число вложенных таблиц [<table>.*</table>]. Требуется "вырезать" по очереди самые вложенные таблицы (не содержащие внутри [<table>.*</table>]), и, соответственно, выводить. И так - рекурсивно до конца вырезать изнутри всю таблицу. Ниже представлена программа, реализующая эту задачу при помощи логического оператора (?!...):
#!/usr/bin/perl -wT
$file=qq|s<table>aaa bbb <table>cc<table>ccc <table> 2<table>bb</table> <table>cc</table> </table></table>cc </table> ddd</table>d |;
print $file; &req($file); sub req { if($file=~m%(<table>((?!.*<table>).*?)</table>)%igs){ $file=~s%(<table>((?!.*<table>).*?)</table>)%%igs; print "Virezali --$1--"; &req($file); } return $file; }
Продолжаем рассматривать логические операторы в регулярных выражениях на опретаорах типа OR, AND или NOT.
Регексп истиннен, если /AM|BMA/ или /AM/ /BMA/ и если есть перекрытие типа /BMAM/. Так-же и /AM/ && /BMA/:
/^(?=.*AM)(?=.*BMA)/s
Выражение истинно если /AM/ и /BMA/ совпадают при перекрытии которое не разрешено:
/AM.*BMA|BMA.*AM/s
Выражение истинно, если шаблон /ABC/ не совпадает:
!~/ABC/
или
/^(?:(?!ABC).)*$/s
Выражение истинно, если ABC не совпадает, а VBN совпадает: /(?=^(?:(?!ABC).)*$)VBN/s
Несовпадение можно проверить несколькими способами:
unless($str =~ /MMM/){...} if(!($str =~ /MMM/)){...} if($str !~ /MMM/){...}
Для обязательного совпадения в двух шаблонах:
unless ($str !~ /MMM/ && $str !~ /BBB/){...} #или if ($str =~ /MMM/ && $str =~ /BBB/){...}
Хотя бы в одном
unless ($str !~ /MMM/ $str !~ /BBB/){...} #или if ($str =~ /MMM/ $str =~ /BBB/){...}
Регулярные выражения - основа работы с операторами m/.../ и s/.../.../, так как они передаются последним в качестве аргументов. Разберемся, как устроено регулярное выражение \b([A-Za-z)+)\b, осуществляющее поиск отдельных слов в строке:
$text = "Perl is the subject."; $text =~/\b([A-Za-z]+)\b/; print $1;
Выражение \b([A-Za-z]+)\b включает в себя группирующие метасимволы ( и ), метасимвол границы слова \b, класс всех латинских букв [A-Za-z] (он объединяет заглавные и строчные буквы) и квантификатор +, который указывает на то, что требуется найти один или несколько символов рассматриваемого класса. Поскольку регулярные выражения, как это было в предыдущем примере, могут быть очень сложными, разберем их по частям. В общем случае регулярное выражение состоит из следующих компонентов:
Совпадение с любым символом
В perl имеется еще один мощный символ - а именно, точка (.). В шаблоне он соответствует любому знаку, кроме символа новой строки. Например, следующая команда заменяет в строке все символы на звездочки (использован модификатор g, обеспечивающий глобальную замену): $text = "Now is the time."; $text =~ s/./*/g; print $text; ********************
А что делать, если требуется проверить совпадение именно с точкой? Символы вроде точки (конкретно, \|()[{^$*+?.), играющие в регулярном выражении осббую роль) называются, как уже было сказано выше, метасимволами, и если вы хотите, чтобы они внутри шаблона интерпретировались как обычные символы, метасимволу должна предшествовать обратная косая черта. Точно так же обратная косая черта предшествует символу, используемому в качестве ограничителя для команды m/.../, s/.../.../ или tr/.../.../, если он встречается внутри шаблона и не должен рассматриваться как ограничитель. Рассмотрим пример:
$line = ".Hello!"; if ($1ine =- m/\./) { print "Shouldn't start a sentence with a perlod!\n"; } Shouldn't start a sentence with a perlod!
Если нужно найти самый короткий текстовый фрагмент /QQ(.*?)FF/ в "QQ ff QQ ff FF", однако оно найдет "ff QQ ff". Шаблон всегда находит левую строку минимальной длины, которая соответствует всему шаблону, т.е. это вся строка в этом примере. Для правильного шаблона нужно воспользоваться логическими операторами в регулярных выражениях: /QQ((?:(?!QQ).)*)FF/, т.е. сначала QQ, потом не QQ, потом FF.
Конструкции (?<=шaблoн) и (?<!шаблон) работают только с шаблонами, соответствующими фиксированному числу символов. Иными словами, в шаблонах, указываемых для (?<=...) и (?<!...), не должно быть квантификаторов.
Эти условия полезны, если нужно проверить, что перед определенным фрагментом текста или после него находится нужная строка, однако ее не требуется включать в результат поиска. Это бывает необходимо, если в коде используются спе-циальные переменные $& (фрагмент, для которого найдено соответствие между текстом и регулярным выражением), $` (текст, предшествующий найденному фрагменту) и $' (текст, следующий за найденным фрагментом). Более гибким представляется применение нумерованных переменных $1, $2, $3, ... в которые заносятся отдельные части найденного фрагмента.
В следующем примере ищется слово, за которым следует пробел, но сам пробел не включается в результат поиска: $text = "Маrу Tom Frank "; while ($text =~ /\w+(?=\s)/g) {print $& . "\n";} Маrу Tom Frank
Того же результата можно добиться, если заключить в круглые скобки интересу-ющую нас часть шаблона и затем использовать ее как переменную $1: $text = "Mary Tom Frank "; while ($text =~ /(\w+)\s/g) { print $1 . "\n"; } Маrу Tom Frank
Следует четко понимать, что вы имеете в виду, когда используете то или иное условие. Рассмотрим следующий пример:
$text="Mary+Tom"; if($text=~m|(?!Mary\+)Tom|){ print "Tom is without Mary!\n"; } else{ print "Tom is busy...\n"; }
Вопреки нашим ожиданиям, perl напечатает: Tom is without Mary!
Это произойдет по следующей причине. Пробуя различные начальные точки входной строки, от которой начинается сопоставление шаблона и текста, pеr1 рано или поздно доберется до позиции, расположенной прямо перед именем "Tom". Условие (?!Маry\+) требует, чтобы после текущей точки не находился текст *Маry+", и это условие для рассматриваемой точки будет выполнено. Далее, perl последовательно проверяет, что после текущей точки следуют буквы "Т", "o" и "m", и это требование также в силе (после проверки условия (?!Маry\+) текущая точка остается на месте). Тем самым найдено соответствие между подстрокой "Тоm" и шаблоном, поэтому команда поиска возвращает значение истина.
Регулярное выражение (?!Mary\+)....Tom, резервирующее четыре символа под текст "Маry+", для приведенного выше случая выведет то, что требовалось, но выдаст ошибочный ответ, если перед именем "Тоm" нет четырех символов:
$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...
Вспомнить и написать про строчку вида
push @mass, $li unless($li=~m/(([2 .. 12]).*?1995)|(([6 .. 12]).*?2001)|/); perldoc perlop [0-9.]
Модификаторы команд m/.../ и s/.../.../
В perl имеется несколько модификаторов, используемых с командами m/.../ и s/.../.../:
i - игнорирует различие между заглавными и строчными буквами.
s - метасимволу "точка" разрешено соответствовать символам \n.
m - разрешает метасимволам ^ и $ привязываться к промежуточным символам \n, имеющимся в тексте. Не влияет на работу метасимволов \А, \Z и \z.
х - игнорирует "пробельные символы" в шаблоне (имеются в виду "истинные" пробелы, а не метасимволы \s и пробелы, созданные через escape-последовательности). Разрешает использовать внутри шаблона комментарии.
g - выполняет глобальный поиск и глобальную замену.
с - после того как в скалярном контексте при поиске с модификатором g не удалось найти очередное совпадение, не позволяет сбрасывать текущую позицию поиска. Работает только для команды m/.../ и только вместе с модификатором g.
о - запрещает повторную компиляцию шаблона при каждом обращении к данному оператору поиска или замены, пользователь, однако, должен гарантировать, что шаблон не меняется между вызовами данного фрагмента кода.
е - показывает, что правый аргумент команды s/.../.../ - это фрагменты выполняемого кода. В качестве текста для подстановки будет использовано возвращаемое значение - возможно, после процесса интерполяции.
ee - показывает, что правый аргумент команды s/.../.../ - это строковое выражение, которое надо вычислить и выполнить как фрагмент кода (через функцию eval). В качестве текста для подстановки используется возвращаемое значение - возможно, после процесса интерполяции
Особенности работы команд m/.../ и s/.../.../
До сих пор мы рассматривали регулярные выражения, используемые в качестве шаблонов для команд m/.../ и s/.../.../, и не особо интересовались, как работают эти команды. Настало время восполнить пробелы.
Команда m/.../ ищет текст по заданному шаблону. Ее работа и возвращаемое значение сильно зависят от того, в скалярном или списковом контексте она используется и имеется ли модификатор g (глобальный поиск).
Команда s/.../.../ ищет прототип, соответствующий шаблону, и, если поиск оказывается успешным, заменяет его на новый текст. Без модификатора замена производится только для первого найденного совпадения, с модификатором g выполняются замены для всех, совпадений во входном тексте. Команда возвращает в качестве результата число успешных замен или пустую строку (условие ложь false), если ни одной замены сделано не было. В качестве анализируемого текста используется $_ (режим по умолчанию) или выражение, присоединенное к шаблону с помощью оператора =~ или !~. В случае поиска (команда m/.../) конструкция, расположенная слева от операторов =~ или !~, может и не быть переменной. В случае замены (команда s/.../.../) в левой части должна стоять скалярная переменная, или элемент массива, или элемент хэша, или же команда присвоения одному из указанных объектов.
Вместо косой черты в качестве ограничителя для аргументов команд 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 nere.\n";} continue { reset if eof; #очистить для следующего файла }
Диагностическое сообщение будет напечатано только один раз, даже если в файле присутствует несколько пустых строк. Команда поиска с вопросительным знаком относится к подозрительным командам, а потому может не войти в новые версии perl. 1 В качестве ограничителей можно также использовать различные (парные) койструкции скобок: while (<>){ if(m/^quit$/i){exit;} if(m(^stop$)i){exit;} if(m[^end$]i) {exit;} if(m{^bye$}i) {exit;} if (!1)<^ехit$>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/.../.../ являются регулярные выражения, которые перед началом работы интерполируются подобно строкам, заключенным в двойные кавычки В отличие от текстовых строк, для шаблона не выполняется интерполяция имен типа $), $| и одиночного $ - perl считает, что такие конструкции соответствуют метасимволу конца строки, а не специальной переменной. Если же в результате интерполяции шаблон поиска оказался пустой строкой, perl использует последний шаблон, который применялся им для поиска или замены.
Если вы не хотите, чтобы perl выполнял интерполяцию регулярного выражения, в качестве ограничителя надо использовать апостроф (одиночную кавычку), тогда шаблон будет вести себя, как текстовая строка, заключенная в апострофы. Однако, например, в случае команды замены 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/.../.../ указать модификатор е, то в качестве второго аргумента надо указать код, который необходимо выполнить (например, вызвать функцию). Полученное выражение будет использованокак текст для подстановки. При этом после вычисления текстового значения, но пер д его подстановкой будет выполнен процесс интерполяции, аналогичный процессу интерполяции текстовых строк, заключенных в двойные кавычки. Еще более сложная схема реализуется, если задан модификатор ее. В этом слу-чае второй аргумент команды s/.../.../ - это строковое выражение, которое сперва надо вычислить (то есть интерполировать), затем выполнить в качестве кода (вызвав встроенную функцию 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 комaндa m/.../ ведет себя сивершенно особым образом. Специальная переменная $_ или переменная, стоящая слева от оператора =~ или !~, при поиске с модификатором g получает дополнительные свойства - в нее записывается последнее состояние. При каждом последующем обращении к данному фрагменту кода поиск будет продолжаться с того места, на котором он остановился в последний раз. Например, следующая команда подсчитывает количество букв х в заданной строке текста:
$text = "Here is texxxxxt."; $counter = O; while ($text =~ m/x/g){ print "Found another x.\n"; $conter++; print "Total amount = $counter.\n"; Found another х. Found another х. Found another x. Found another x. Found another x. Total amount = 5.
Состoяние (точнее, позиция) поиска сохраняется даже в случае перехода к следующему оператору поиска, имеющему модификатор g. Неудачный поиск сбрасывает значение в исходное состояние, если только для команды m/.../ не указан модификатор с (то есть команда должна иметь вид m/.../gc). Изменение текстового буфера, для которого выполняется поиск, также сбрасывает позицию поиска в исходное состояние. В следующем примере из текстовой строки последовательно извлекаются и выводятся пары имя/значение до тех пор, пока строка не закончится:
$text = "X=5; z117e=3.14l6; temp=lQ24;"; $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 while /(p)/gc; print "', pos=",pos, "\n"; }
1: 'oo', pos=4; 2: 'q', pos=7; 3: 'pp', pos=4; 1: '', pos=7; 2: 'q', pos=8; 3: '', pos=8;
В документации perl приводится основанный на этом механизме интересный пример последовательного лексического разбора текста. В нем каждая последующая команда поиска очередной лексической единицы начинает выполнятьсяс того места, где завершила свою работу предыдущая. Советую внимательно разобраться с этим примером (страница руководства perlop, раздел "Regexp Quote-Uke Operators", описание команды m/PATTERN/), если вы хотите расширить доступный вам инструментарий perl!
Замена строк с помощью команды tr/.../.../
Кроме команд m/.../ и s/.../.../ строки можно обрабатывать с помощью команды tr/.../.../ (она же - команда у/.../.../):
tr/список1/список2/модификаторы; у/список1/список2/модификаторы;
В отличие от m/.../ и s/.../.../, эта команда не использует шаблоны и регулярные выражения, а выполняет посимвольную замену, подставляя в текст вместо литер из первого списка соответствующие им литеры из второго списка. Например, в следующем случае производится замена литер "i" на "о":
$text = "My name is Tim."; $text =~ tr/i/o/; print $text; My name is Tom.
В качестве списков используются идущие друг за другом символы, не разделяемые запятыми (то есть это скорее строки, чем списки). В отличие от шаблонов команд m/.../ и s/.../.../, аргументы команды tr/.../.../ не интерполируются (то есть подстановки значений вместо имен переменных не происходит), хотя escape-последовательности, указанные внутри аргументов, обрабатываются правильно. Подобно m/.../ и s/.../.../, команда tr/.../.../ пo умолчанию работает с переменной $_: while (<>){ tr/iI/jJ/; print;
В качестве списков можно указывать диапазоны символов - как, например в следующем фрагменте кода, заменяющем строчные буквы на заглавные: $text = "Here is the text."; $text =~ tr/a-z/A-Z/; print $text; HERE IS THE TEXT.
Как и в случае m/.../ u s/.../.../, команда tr/.../.../ не требует использовать именно знаки косой черты в качестве ограничителей. Можно использовать практически любой символ, отличный от "пробельных", букв и цифр, а также парные скобочные конструкции.
Команда tr/.../.../ возвращает число успешных замен. В частности, если не было сделано никаких замен, она возвращает число ноль. Это позволяет, например, подсчитать с помощью команды tr/.../.../ количество вхождений буквы х в строку $text, не меняя содержимого этой переменной: $text = "Here is the text."; $xcount = ($text =~tr/x/x/); print $xcount; 1
Если у команды tr/.../.../ нет модификаторов (см. далее раздел "Модификаторы команды tr/.../.../"), то ее аргументы при обычных условиях должны быть одинаковой длины. Если второй аргумент длиннее первого, то он усекается до длины первого аргумента - так, команда tr/abc/0-9/ эквивалентна команде tr/abc/012/. Если первый аргумент длиннее второго и второй не пуст, то для второго аргумента необходимое число раз повторяется его последний символ - так, команда tr/O-9/abc/ эквивалентна команде tr/0123456789/abcccccccc/. Если же второй, аргумент пуст, то команда tr/.../.../ подставляет вместо него первый аргумент.
Как легко заметить, если второй аргумент пуст, то (при отсутствии модификаторов) команда tr/.../.../ не производит никаких действий, а возвращаемое ею значение равно числу совпадений между первым аргументом и обрабатываемым текстом. Например, следующая команда подсчитывает количество цифр в строке: $text = "Pi=3.1415926536, е=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 - удаляет непарные символы, не выравнивая аргументы по длине.
с - в качестве первого аргумента использует полный список из 256 символов за вычетом указанных в списке символов.
s - удаляет образовавшиеся в результате замены повторяющиеся символы.
Если указан модификатор d, a первый аргумент команды длиннее второго, то все символы из первого списка, не имеющие соответствия со вторым списком, удаляются из обрабатываемого текста. Пример: удаляем строчные латинские буквы и заменяем пробелы на слэши: $text = "Here is the text."; $text =~ tr[ a-z][/]d; print $text; H///.
Наличие модификатора d - единственный случай, когда первый и второй аргументы не выравниваются друг относительно друга, В остальных вариантах второй аргумент либо усекается, либо последний символ в нем повторяется до тех пор, пока аргументы не сравняются, либо, если второй аргумент пуст, вместо Второго аргумента берется копия первого.
Если указан модификатор с, то в качестве первого аргумента рассматриваются все символы, кроме указанных. Например, заменим на звездочки все символы, кроме строчных латинских букв: $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; / / / /. Без модификатора s результат был бы другим: $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-\l77}; $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 = "How 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/"exlt$/) {exit;} }
Поиск чисел
Для проверки того, действительно ли пользователь ввел число, можно использо-вать метасимволы \d и \D. Метасимвол \D соответствует любому символу, кроме цифр. Например, следующий код проверяет, действительно ли введенный текст представляет собой целое значение без знака и паразитных пробелов: $test = "Hello!"; if($text =~ /\D/){ print "It is not a number.\n"; } It is not a number. To же самое можно проделать, использовав метасимвол \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";
Поскольку плюс является метасимволом, его надо защищать обратной косой чертой. Однако внутри квадратных скобок, то есть класса символов, он не может быть квантификаторам. Знак "минус" внутри класса символов обычно играет роль оператора диапазона и поэтому должен защищаться обратной косой чертой. Однако в начале или в конце шаблона он никак не может обозначать диапазон, и поэтому обратная косая черта необязательна. Наконец, более строгая проверка, требует, чтобы знак, если он присутствует, был только один:
$text = "+0.142857142857142857"; if ($text =~ /^(+|-|)\d+(\.\d*\)$/) { print "It is a number.\n"; } It is a number.
Альтернативные шаблоны, если они присутствуют, проверяются слева направо. Перебор вариантов обрывается, как только найдено соответствие между текстом и шаблоном. Поэтому, например, порядок альтернатив в шаблоне (\.\d*|) мог бы стать критичным, если бы не привязка к концу строки. Наконец, вот как можно произвести проверку того, что текст является шестна-дцатеричным числом без знака и остальных атрибутов: $text = "1AO"; unless (ftext =~ m/^[a-fA-F\d]+$/) { print "It is not a hex number, \n"; }
Проверка идентификаторов
С помощью метасимвола \w можно проверить, состоит ли текст только из букв, цифр и символов подчеркивания (это те символы, которые perl называет словесными (word characters)): $text="abc"; if($text=~/^\w+$/){ print "Only word characters found. \n"; } Only word characters found.
Однако, если вы хотите убедиться, что текст содержит латинские буквы и несодержит цифр или символов подчеркивания, придется использовать другой шаблон: $text = "аbс"; if($text=~ /^[A-Za-z]+$/) { print "Only letter characters found.\n";} Qnly letter characters found.
Наконец, для проверки, что текст является идентификатором, то есть начинаетcя с буквы и содержит буквы, цифры и символы подчеркивания, можно испольpовать команду:
$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/.../ будет упорно находить первое вхождение буквы х, и цикл будет продолжаться бесконечно.
В отличие от команды m/.../ команда s/.../.../ с модификатором g выполняет глобальную замену за один раз, работая так, будто внутри нее уже имеется встроенный цикл поиска, подобный приведенному выше. Следующий пример за один раз заменяет все вхождения х на z: $text = "Here is texxxxxt."; $text =~ s/x/z/g; print $text; Here is tezzzzzt.
Без модификатора g команда s/.../.../ заменит только первую букву х. Команда 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"; } product's type is printer.
Вызов функций и вычисление выражений при подстановке текста
Используя для команды s/.../.../ модификатор е, вы тем самым показываете, что правый операнд (то есть подставляемый текст) - это то выражение perl, которое надо вычислить. Например, с помощью встроенной функции perl uc (uppercase) можно заменить все строчные буквы слов строки на заглавные: $text = "Now is the time."; $text=~ s/(\w+)/uc($1)/ge; print $text; NOW IS THE TIME. Вместо функции uc($l) можно поместить произвольный код, включая вызовы программ.
Поиск n-го совпадения
С помощью модификатора g перебираются все вхождения заданного шаблона. Но то делать, если нужна вполне определенная точка совпадения с шаблоном, например, вторая или третья? Оператор цикла while в сочетании с круглыми cкобками, выделяющими нужный образец, поможет вам: $text = "Name:Anne Nanie:Burkart Name:Glaire Name: Dan"; 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:Ciaire Name:Dan"; for ($match = 0; $text =~ /Name:\s*(\w+)/g; print "Match number ${\match} is $1.\n") {} Match nuwber 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:ClaireName:Dan
В процессе глобального поиска при каждом найденном совпадении вычисляется выражение, указанное в качестве второго операнда. При его вычислении увеличивается значение счетчика, и в зависимости от него в качестве замены подставляется либо старое значение текста, либо новое. Модификатор х позволяет добавить в поле шаблона комментарии, делая код более прозрачным. Обратите внимание, что нам пришлось заключить весь шаблон в круглые скобки, чтобы получить значение найденного текста и подставить его на прежнее место полностью.
Как ограничить "жадность" квантификаторов
По умолчанию квантификаторы ведут себя как "жадные" объекты. Начиная с текущей позиции поиска, они захватывают самую длинную строку, которой может соответствовать регулярное выражение, стоящее перед квантификатором. Алгоритм перебора с возвратами, используемый 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,}? - по крайней мере n совпадений,
{n,m}? - совпадений по крайней мере 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.
Чтобы отсечь и начальные, и хвостовые пробелы лучше вызвать последователно эти две команды, чем использовать шаблон, делающий отсечение ненужных пробелов за один раз. Поскольку процедура сопоставления шаблона и текста достаточно сложна, на эту простую операцию может уйти гораздо больше времеви, чем хотелось бы.
Например в тексте нужно найти текст, находящийся между открывающим и закрывающим тегом:
$text="<a>blah-blah</a>"; if($text=~m!<([a|b])>(.*?)/\1!ig){ print "$2\n"; }
найдет все слова, стоящие между тегами <a></a> и <b></b>.
В регулярных выражениях пристутствует своя семантика: быстрота, торопливость и возврат. Если квантификатор * совпадает во многих случаях, то в результате быдет выведен наибольший по длинне результат. Это жадность. Быстрота: поиск старается найти как можно быстрее. "Text"=~/m*/, по смыслу символов m нет, но в результате будет возвращено значение 0. Т.е. формально 0 и более символов.
$test="aaooee ooaao"; $test=~s/o*/e/; print $test; eaaooee ooaao
потому что 1 элемент сторки - 0 и более символов.
Если добавить квантификатор g, то результат будет таким:
eaeaeeeeee eeaeaee
т.к строка содержит 13 мест, где может встречатся o, в том числе и пустых.
Модификаторы:
/i игнорировать регистр
/x игнорировать пропуски в шаблоне и разрешить комментарии.
/g модификатор разрешающий выполнение поиска/замены везде, где это возможно
/gc не сбрасывается позиция при неудачном поиске.
/s разрешается совпрадение . с \n, игнорируется $*.
/m разрешить совпадение ^ и $ для начала и конца строки во внутренних переводах строк
/o однократная компиляция
/e правая часть s/// представляет собой выполняемый код
/ee правая часть s/// выполняется, после чего возвращаемое значение интерпретируется снова.
при вызове use locаle учитываются локальные настройки. Модификатор /g может заполнить массив значений @nums = m/(\d+)/g; но это сработает для ненакладывающихся совпадений. Чтобы поймать совпадения нужно воспользоваться оператором ?=... Если ширина = 0, то механизм поиска остался на прежнем месте. Найденые данные остаются внутри скобок. Если есть модификатор /g, то текущая позиция остается прежней, но происходит перемещение на один символ вперед.
$numbers="123456789"; @one=$numbers=~/(\d\d\d)/g; @two=$numbers=~/(?=(\d\d\d))/g; print "@one \n"; print "@two \n";
Модификаторы m и s нужны для поиска последовательностей символов, содержащих перевод строки. При s точка совпадает с \n и игнорируется $*. m делает совпадающими ^ и $ до и после \n. e правая часть выполняется как программный код: perl -i -n -p -e 's/(.)/lc($1)/g' *.html приводит все литеры во всех файлах *.html текущей директории к нижнему регистру.
Встроенные переменные в regex.
$1, $2, $3, $4, ..., $n ... содержат ссылки на найденный текст, только в том случае если regex был в круглых скобках:
s%<f(.*?)><(.*?)"><(.*?)">%$1 $2 $3%g;
внутри regex можно использовать переменные типа \1, \2, \3, \4, ... \n, ...
s/a href=(["'])(.*?)\1>/$2/g
найдет все урл, заключенные в двойные, одинарные и вообще без кавычек, находящиеся в документе.
для /(a.*b)|(mumu)/ в переменной $+ содержится $1 или $2.
$& содержит полный текст совпадения при последнем поиске.
$' и $` содержатся строки до и после совпадения
Если нужно скопировать и сделать подстановку, то нужно действовать примерно так:
($at = $bt) =~ s!m(.*?)o!! #для строк for(@mass1 = @mass2){s/umka/maugli/} #для массивов
$u = ($m=~s/a/b/g); #поменять $m и занести в $u число замен.
Если нужно выцепить только алфавитные символы, с учетом настроек locale, то регексп примерно такой: /^[^\W\d_]+$/ в нем учитываются все не алфавитные символы, не цифры и не подчеркивания(для случая "ванька-встанька"), симвлол отрицания в группе [] - ^, т.е. найти все, что не [\W\d_], можно было написать и скажем так !~m/^(\W|\d|_)*/.
Для упрощения понимания сложных регулярных выражений можно воспользоваться их комментированием. Иногда правда можно только по виду регулярного выражения определить зачем оно предназначено:
$mmm{$1} = $2 while ($nnn =~ /^([^:]+):\s+(.*)$/m);
читаем регулярное выражение:
нужно найти в файле все что до двоеточия не двоеточие и все что после двоеточия(включая возможные повторения после первого : .*?: .*?: .*?:, потому что была найдена первая позиция: выделить все что не есть двоеточие до первого двоеточия)
Что это может быть, вполне вероятно, что оно нужно для составления статистики писем, выцепление заголовка письма и его названия из mbox в хеш. По крайней мере это регулярное выражение подходит для данной задачи.
Манипуляции с файлом
Открытие файла осуществляется функцией open.
open(FFF,"> file.txt");
Разберемся. У функции три параметра: FFF - название файлового манипулятора (его задаете вы), режим доступа ">" и "file.txt" - имя нужного вам файла. Есть три основных режима: ">" - запись,">"- добавление в файл.
Есть еще функция sysopen. Работа с ней не на много сложнее, чем с open, зато с ее помощью вы сможете более детально "сказать" системе о ваших намерениях (то есть то, что вы хотите сделать с файлом).
В sysopen три обязательных параметра и один необязательный.
Например: sysopen(FH,$name, $flags, $param);
FH - собственно, файловый манипулятор, $name - имя файла в чистом виде (без ">" и др.). В $flags помещаем число, полученное объединением нижеописанных констант через OR ( | ):
O_RDONLY | Только для чтения |
O_WRONLY | Только для записи |
O_RDWR | Для чтения и для записи |
O_CREAT | Если файла нет, создать! |
O_APPEND | Открытие в режиме присоединения |
O_TRUNC | Очищаем содержимое при открытии |
Это, конечно, не полный перечень, но здесь перечислены самые необходимые и часто используемые константы.
И наконец $param. Этот параметр задает маску доступа к файлу и записывается в восьмеричной системе. Обычно используется значение 0666 (значение по умолчанию, то есть если $param опущен), или 0777. Первое значение используется для обычных файлов, второе же для каталогов и исполняемых файлов.
Пример открытия файла для записи (если не найден - создается): sysopen(FH,"data.txt",O_WRONLY|O_TRUNC|O_CREATE);
Запись в файл делаем функцией print.
print(FFF "oppa! Пишем в файл!");
Здесь FFF - имя файлового манипулятора, а строка в кавычках - текст, который мы хотим записать в файл, ассоциированный с FFF.
Если до попытки открытия файла не существовало, то функция open его создаст, а если файл был, и он был не пустой, то после вышеуказанной функции print, в нем ничего не останется от предыдущей информации, а записана будет та ерунда, которую я там вписал.
Как уже было сказано, существуют три стандартных файловых манипулятора, и при опущенном файловом манипуляторе функция print осуществит вывод в STDOUT (то же относится к функциям printf и write). Чтобы изменить направление вывода в Perl предусмотрена функция select (правда не только для этого). Пример:
open(F1,"> one.txt"); print "Файл открыт! Пишем в STDOUT."; $old_point=select(F1); print "Пишем в файл one.txt"; select($old_point); print "Снова пишем в STDOUT."; close(F1);
Закрываем файл функцией close. close(FFF);
Принцип убирать за собой прививается всем с детства. Давайте не забывать об этом и в программировании, хотя при завершении процесса, в котором был открыт данный файл, файл закрывается автоматически.
Блокировка файла
Во-первых для чего? А для того, что если несколько процессов хотят одновременно заполучить доступ к файлу, причем на запись, причем еще и хотят туда что-то писать (кошмар), то представьте, что оказалось бы, если не этот чудный механизм блокировки. Он позволяет, грубо говоря, ставить процессы в очередь. Делаем так:
open(FIL,"> file.dat"); flock(FIL,2); close(FIL);
О функциях open и close уже говорили, а на flock остановимся немного подробнее. Она получает два параметра - файловый манипулятор и, образно говоря, категорию блокировки. 1 совместная блокировка (для чтения) 2 монопольная блокировка (для записи) 4 асинхронный запрос блокировки 8 снятие блокировки
Про снятие блокировки: блокировка автоматически снимается при завершении процесса, вызванного текущим скриптом, либо при закрытии файлового манипулятора, который "привязан" к заблокированному файлу. Если вы снимаете блокировку вручную, будьте аккуратны - вы даете возможность другому процессу делать с (ВНИМАНИЕ!) не закрытым вами файлом все что ему угодно! Последствия могут быть, мягко говоря, неприятные, а грубо говоря - непредсказуемые (для вас непредсказуемые).
Map
Функция map похожа по своей работе на обычное условие if, допустим нужно разделить записи на блоки, разделенные четырьмя пробелами:
@probel = map m!\s{4}!, split /\n/, $test;
Массивы хэшей
Массивы хэш-таблиц позволяют индексировать числовым значением записи с
именованными полями. В следующем примере создается массив хэшей:
@array = (
{
favorite => "apples",
'second favorite' => "оranges"
},
{
favorite => "corn",
'second favorite' => "peas",
'last favorite' => "turnip"
},
{
favorite => "chiken",
'second favorite' => "beef"
}
print $array[0]{favorite};
Обратите внимание, что для конструкций вида [...]{...},
как и для рассматриваемых ранее конструкций
вида {...}{...} и
[ ][[ ], между парами скобок неявно
подставляется оператор-стрелка -> разыменования ссылок.
Создание массива хэшей на лету
Можно создавать массивы хэшей шаг за шагом, присваивая
ссылки на анонимные хэши элементам массива:
@аггау[0] = {favorite => "apples",
'second favorite' => "oranges"};
@array[1] = {favorite => "corn",
'second favorite' => "peas",
'least favorite' => "turnip"};
@array[2] = {favorite => "chicken",
'second favorite' => "beef"};
print $array[0]{favorite};
Как и в случае массива массивов, вы можете воспользоваться функцией push:
push @array, {favorite => "apples",
'second favorite' => "oranges"};
push @array, {favorite => "corn",
'second favorite' => "peas",
'least favorite' => "turnip"};
push @array, {favorite => "chicken",
'second favorite' => "beef"};
print $array[0]{favorite};
В следующем примере мы последовательно читаем из текстовых строк пары
ключ/значение и превращаем их в массив хэшей:
$data[0] = "favorite:apples, second favorite:оranges";
$data[1] = "favorite:corn, second favorite:peas, least favorite:turnip";
$data[2] = "favorite:chicken, second favorite:beef";
for $loopindex (O..$#data) {
for $element(split ',', $data[$loopindex]){
($key, $value) = split ':', $element;
$key=~s/^[\s\n]+//; #очистить от пробелов
$key=~s/[\s\n]+$//;
$value =~s/^[\s\n]+//; #очистить от пробелов
$value =~s/[\s\n]+$//;
$array[$loopindex]{$key} = $value;
}
}
print $array[0]{'second favorite'};
Обратите внимание, что мы здесь воспользовались контекстно-чувствительной
процедурой автооживления ссылок (autovivification)
Доступ к элементам массива хэшей
Чтобы получить значение, хранимое; в массиве хэшей, надо указать индекс
массива и ключ хэша:
$array[0] = {favorite => "apples",
'second favorite' => "oranges"};
$array[1] = {favorite => "corn",
'second favorite' => "peas",
'least favorite' => "turnip"};
$array[2] = {favorite => "chicken",
'second favorite' => "beef"};
print $array[0]{favorite};
В следующем случае мы полностью выводим массив хэшей с помощью цикла по
его элементам:
$array[0] = {favorite => "apples",
second => "oranges"};
$array[1] = {favorite => "corn",
second => "peas",
least => "turnip"};
$array[2] = {favorite => "chicken",
second => "beef"};
for $loopindex (0..$#array) {
print "array[$loopindex]:\n\t{";
fоr $key (keys %{$array[$loopindex]})
{
print "$key => $array[$loopindex]{$key},";
}
print "}\n";
A вот как сделать то же самое, используя вместо индекса цикла ссылку:
$array[0] = {favorite => "apples",
second => "oranges"};
$array[1] = {favorite => "corn",
second => "peas",
least => "turnip"};
$array[2] = {favorite => "chicken",
second => "beef"};
for $hashreference(@array) {
print "{";
for $key (sort keys %$hashreference) {
print "$key => $array[$loopindex]{$key}, ";
}
print ,"}\n";
}
Мнимые символы
Мнимые символы в регулярных выражениях
В perl имеются символы (метасимволы), которые соответствуют не какой-либо литере или литерам, а означают выполнение определенного условия (поэтому в английском языке их называют assertions, или утверждениями). Их можно рассматривать как мнимые символы нулевого размера, расположенные на границе между реальными символами в точке, соответствующей определенному условию:
^ - начало строки текста,
$ - конец строки или позиция перед символом начала новой строки, распо-ложенного в конце,
\b - граница слова,
\В - отсутствие границы слова,
\А - "истинное" начало строки,
\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, то текстовое выражение будет рассматриваться как многострочный текст, в котором границами строк выступают символы новой строки \n. В случае многострочного текста метасимвол ^ сопоставляется с позицией после любого символа новой строки, а не только с началом текстового выражения. Точно также метасимвол $ - это позиция перед любым символом новой строки, расположенным внутри текстового выражения, а не обяательно конец текстового выражения или же позиция перед концевым символом \n. Однако метасимвол \A - начало текстового выражения, а метасимвол \Z - конец текстового выра-жения или позиция перед концевым символом \n, даже если в текстовом выражении имеются вложенные символы \n и при выполнении операции поиска или йены указан модификатор m. Метасимвол точка (.) соответствует любому символу, кроме символа новой строки \n. Независимо от того, задан ли модификатор m, она не будет сопоставляться ни c внутренними, ни с концевыми символами \n. Единственный способ заставить точку рассматривать \n как обычный символ - использовать модификатор s.
Отсюда понятна разница между метасимволами \Z и \z. Если в качестве текстового выражения используется результат чтения входного потока данных, то с большой вероятностью данное выражение заканчивается символом \n, за исключениeм того варианта, когда программа предусмотрительно "отщипнула" его с помощью функции chop или chomp. Метасимвол \Z игнорирует концевой символ \n если он случайно остался на месте, рассматривая обе ситуации как "конец строки". В отличие от него метасимвол \z оказывается более пунктуальным и рассматривает концевой символ \n как неотъемлемую часть проверяемого текстового выражения, если только пользователь не позаботился об удалении этого символа.
Отдельно следует остановиться на метасимволе \G. Он может указыватьсяв регулярном выражении только в том случае, если выполняется глобальный поиск (то есть если команда m/.../ имеет модификатор g). Метасимвол \G, указанный в шаблоне, соответствует точке, на котброй остановилась предыдущая операция поиска.
Несколько советов по отладке
CGI-программы -не самые простые в отладке,по сложности отладки они способны сравнится лишь с отладкой драйверов. Вся сложность заключается в том,что скрипт выполняется не как обычная программа. Он выполняется в специальной среде сервера,которая создается при клиентском запросе, к тому же он исполняется не из под вашего аккаунта,а на непривилегированом уровне.
Если скрипт не исполняется потому,что вы допустили синтаксические ошибки,то самих этих ошибок вы не увидите,на экране будет только 'Internal Server Error' из-за чего она произошла вы можете только гадать. Также если вы забыли задать к какому-то файлу нужные права доступа ,то тоже будет трудно выяснить что же произошло и в чем причина ошибки (если конечно к этому вы не готовы).
Ну вот ,хватит вас пугать,тем более что нас не запугаешь ;) !
Приступим к отладке.Я вам опишу достаточно примитивные меры,которыми я сам пользуюсь.
Начнем с того что у нас есть скрипт test.cgi мы уже сделали его исполняемым chmod +x test.cgi
Простейший способ проверить его на ошибки это команда perl -c test.cgi
Ключ -c говорит Perl что надо только проверить синтаксис.Все сообщения об ошибках вы можете видеть и подправить.Более тяжелый случай состоит в том когда Perl встроен в Web -Сервер, причем версии разные.Как у нас на uic'е ;(( ! Тот Perl с которым работаем в командной строке
4й версии ,а на сервере стоит 5й версии.Если ваша CGI-программа использует при этом какие-нибудь преимущества 5-й версии (например обьектно-ориентированые модули),то вы думаете отладить ее низя -ошибаетесь!.Только приготовтесь к тому, что я сейчас скажу,вы сядте,а то упадете ;)) :
Закоментируйте всю вашу программу ,т.е. перед каждой строчкой поставьте символ '#'. После чего,добавьте вот такие строчки: print "Content-Type: text/html\n\n";
print "<HTML>Test</HTML>"; exit; ,Должно получится так:
#!/usr/bin/perl #test.cgi print "Content-Type: text/html\n\n"; print "<HTML>Test</HTML>"; exit; #Программа как вы понимаете выполняется только до етого места # #if($ENV{'REQUEST_METHOD'} eq 'GET'){$query=$ENV{'QUERY_STRING'}} #else{sysread STDIN,$query,$ENV{'CONTENT_LENGTH'};} #if($query eq ''){ # @formfields=split /&/,$query; # ....... # ........
А теперь запускайте скрипт. Естественно он выдаст Одно только слово 'Test'. Разкоментируйте несколько строчек.Еще раз запустите скрипт.Он опять выдаст 'Test'. Значит синтаксически эти только что разкоментированые строчки были правильные. И так далее....
Если очередной раз после раскоментирования вы запустили скрипт и получили 'Internal Server Error' - значит в этих строках содержалась какая-та синтаксическая ошибка. Это способ отловки синтаксических ошибок трудоемок,но к нему придется прибегнуть если ваш скрипт писан под ту версию Perl,что на сервере,а не под ту что у вас.
Узнать версию Perl можно perl -v
Ну вот мы отловили в нашем скрипте все синтаксические ошибки,он заработал, но это не значит,что он работает правильно. Что еще можно посоветовать при отладке CGI-скриптов от ошибок возникающих во время выполнения программы. Допустим какой-то файл не открылся.Конечно показывать перепуганому пользователю эти технические подробности никчему,поэтому заведите себе специальный файл debug.txt
и пусть ваши скрипты пишут в этот файл причины своих ошибок и сбоев, да и вообще о всех непредвиденых событиях.
Это можно реализовать так:
sub debug_err{ open DEBUGFILE,">>debug.txt"; print DEBUGFILE $ENV{'SCRIPT_NAME'}.' '.scalar localtime.' '.@_."\n"; close DEBUGFILE; }
Примеры использования (Напомню,что встроеная переменная Perl $! содержит сообщение о причине последней ошибки,поэтому включайте ее всегда в свои сообщения):
open F,"+<$myfile" debug_err("Cannot open $myfile $!"); seek F,0,0 debug_err("Cannot seek $myfile $!"); connect SOCKET,$paddr debug_err("Cannot connect to $remote $!"); ......
Потом можно периодически заглядывать в этот файл debug.txt и смотреть,какие ошибки встречались при работе ваших скриптов.Таким образом ваши скрипты сами помогать будут в своей отладке ;).
Кстати я хочу сказать о причине еще одной (совсем не очевидной) ошибки.Если вы набрали скрипт у себя дома на компутере,то полученый скрипт состоит из текста в DOS'ом формате, а не в Unix'ом так что имейте это ввиду. Запускать вам его придется в системе Unix , так что следует перевести програмный текст в нужный формат.
Нужно ли для этого знать языки программирования?
Э....я например только в школе Basic проходил - вопросы есть? Конечно, если что-то знаешь, то хорошо, но эти советы не для знающих ;-) Вот что желательно, так это понимать хоть примерно (процентов на 60) надписи на английском - существует огромное количество бесплатных скриптов (см. ссылки в конце статьи), почти все они имеют подробные инструкции по установке, кроме того, в самом коде присутствуют подробные комментарии. Стоит добавить, что наиболее распространенный язык для написания CGI скриптов - это Perl, но не надо думать, что "Говорим CGI - подразумеваем Perl!" Совсем нет! Perl cgi скрипты просто более популярны (возможно из-за того, что Perl - язык программирования под Unix, а большинство серверов в Сети работает под юниксоидными системами, и Perl к тому же, не самый сложный в освоении язык, но при этом предоставляющий мощные и довольно простые в использовании возможности при работе с текстом, строковыми данными и т.д.). Есть CGI скрипты и на C/C++, но это уже для windows'ких серверов, а там более популярна технология ASP, а сейчас речь не об этом…
Объявление
Объявление массивов очень просто:
my @mass();
Но массив, как и хэш, можно и не объявлять, перл это умеет делать сам.
Доступ к элементам массива осуществляется например таким образом(выводится случаный элемент массива):
print $mass[ramd $#mass];
В переменной $#mass содержится размер массива-1, т.к. нуумерация элемента массива начинается с нулевого элемента.
Хэши начинаются с префикса %: %hash. Для работы с с хэш-массивами нужно, как и в обычных массивах, использовать разыменовывающий префикс $: $hash{шляпа} = серая;
Хэш можно определить несколькими способами:
%hash = (
'шляпа' => 'серая',
'водка' => 'горькая',
'вобла' => 'вкусная',
'штаны' => 'широкие',
'пиво' => 'темное',
'игрушка' => 'любимая');
%hash = (); #можно не указывать пустой хеш для создания массива, perl может сам создавать пустой хэш.
$hash{'шляпа'} = 'серая';
$hash{'водка'} = 'горькая';
$hash{'вобла'} = 'вкусная';
$hash{'штаны'} = 'широкие';
$hash{'пиво'} = 'темное';
$hash{'игрушка'} = 'любимая';
Если используется пробел при определении элемента хэша, то этот пробел лучше поставить в одинарные кавычки $hash{'дырявая шляпа'} = серая;
Добавить элементы в хеш можно так:
%hash = (
'шляпа' => 'серая',
'водка' => 'горькая',
'вобла' => 'вкусная');
%hash = (%hash, 'кошка', 'мягкая');
foreach $k (keys %hash){
print "$k = $hash{$k}\n";
}
и так:
%hash = (
'шляпа' => 'серая',
'водка' => 'горькая',
'вобла' => 'вкусная');
$hash{"дом"} = "большой";
$hash{"дым"} = "сизый";
foreach $k (keys %hash){
print "$k = $hash{$k}\n";
}
и еще например так:
my %Years = (
1999 => "golos1999.html",
2000 => "golos2000.html",
2001 => "golos2001.html",
map{$_ => "golos$_.html"}(2002..2032)
);
т.е. если у нас есть файлы, которые группируются по годам, дням, месяцам или еще как,
то дабы не переписывать скрипт, можно сказать
map { $_ => "golos$_.html" } (2002..2032)
где год или неделя берется из какое-йнибудь переменной, в данном случае из $_.
Облегчение поиска работы
Допустим Вы оказались без работы, развалилась ваша фирма или еще какая-нибудь причина. Вам требуется найти новую. Для упрощения этой задачи естьь следующий скрипт, который выцепливает по нужной позиции(веб программирование, зарплата от 200$ и т.д.) с www.job.ru все заявки за последние 10-15 дней, точнее емейлы, куда нужно слать резюме, что значительно убыстряет поиск работы(имея базы адресов легче разослать одно и то-же резюме, используя нехитрый список рассылки):
#!/usr/bin/perl -wT $url0="http://www.job.ru/cgi/list1.cgi?GR_NUM="; $url1="%31&TOPICID=9&EDUC=2&TP=&Gr=&SEX=&AGEMIN=23&AGEMAX=&MONEY=200&CDT="; $url2="&LDAY=99&ADDR=%ED%CF%D3%CB%D7%C1&KWORD=&KW_TP=AND"; use LWP::Simple; foreach($i=1; $i
Что делает эта программа, она составляет GET запрос из параметров, которые скрыты в hidden полях навигации по результатам запроса на www.job.ru. Программа при помощи Simple.pm отправляет запрос на сервер и как бы листает странички с поиском. Критерий ваших профессиональных навыков составлен в GET-запросе и осталось только разослать почту(для этого можно написать список рассылки) по адресам, которые выдала программа. Разберем регулярное выражение для вытаскивания почтового адреса из текущей странички s/(.*) ([\w+\-\.]+\@[\w\-\.]+\.\w{2,3})(.*)/$2/ig.
[\w+\-\.]\@ - найти все что содержит буквы, тире и точки до символа @, ведь почтовый адрес по спецификации может быть вида aa.ss-ss@chto-to.ru. Тоже самое после символа @ - [\w\-\.]+
далее может быть точка \. и любая буква от 2 до 3 символов \w{2,3}, т.е. окончание, самый верхний домен .com, .ru, .cz и т.д. Далее регулярное выражение состоит из трех классов скобок (.*) - переменная $1, ([\w+\-\.]+\@[\w\-\.]+\.\w{2,3}) переменная $2 и все остальное в (.*) - $3. Пробел перед $2 стоит потому, что так устроен html, отдаваемый пользователю поиском по базе предложений о работе www.job.ru. Нам нужно содержимое $2, в котором находится e-mail работодателя. Пишем его во вторую часть s/наш regex/$2/ig. Квантификатор i нужен для того, чтобы не различать регисты Vasya@pupkin.ru и vasya@pupkin.ru, квантиикатор g задействова на тот случай, если работодатель указывает 2 адреса, по которым нужно высылать резюме. На 23 августа 2001 года на 20 часов 10 минут прогамма выдала 410 e-mail адресов(пролистав за 3-4 минуты 57 страниц), где вас ждут, как потенциального сотрудника.
Остается написать скрипт почтовой рассылки по e-mails, выданным данным скриптом. Но это в другой главе.
Примером выше был получен спсиок email адресов. Теперь необходимо проверить, действительно ли существуют домены, на которых заведены такие пользщователи(примитивная - но проверка).
#!/usr/bin/perl use Socket; #загрузить inet_addr s{ # ( #Сохранить имя хоста в $1 (?: #Группирующие скобки (?! [-_] ) #ни подчеркивание, ни дефис [\w-] + #кусок имени хоста \. #и точка домена )+ #повторить несколько раз [A-Za-z] #следующий символ - буква [\w-]+ #домен верхнего уровня ) #конец записи $1 }{ #Заменить следующим: "$1" . #исходн часть + пробел (($addr = gethostbyname($1)) #Если имеется адрес ? "[" . inet_ntoa($addr). "]"#отформатировать : "[???]" #иначе пометить как сомнительный ) }gex
Переписываем исходную программу с учетом вышеприведенного кода
#!/usr/bin/perl -wT $url0="http://www.job.ru/cgi/list1.cgi?GR_NUM="; $url1="%31&TOPICID=9&EDUC=2&TP=&Gr=&SEX=&AGEMIN=23&AGEMAX=&MONEY=200&CDT="; $url2="&LDAY=99&ADDR=%ED%CF%D3%CB%D7%C1&KWORD=&KW_TP=AND"; use Socket; use LWP::Simple; foreach($i=1; $i
Между строчками можно комментировать целые куски кода. =pod $file=~s{((?:(?![-_])[\w-]+\.)+[A-Za-z][\w-]+)} {"$1".(($site=gethostbyname($1))?"[".inet_ntoa($site)."]":"[???]")}gex; print $file,"\n" if($file !~/\?\?\?/); =cut
Эта программа успешно удалила некторые из адресов, которые Socket.pm показались подозрительными. Все-таки какую-никакую, а проверку существования e-mail адресс окольными путями при помощи perl провести можно. Автору сего текста все-таки больше нравится вариант, заключенный в комментарии =pod(.*?)=cut. Он просто короче. Да и если научится читать сложные регулярные выражения, то можно написать полный регексп е-mail адресов, который занимается тем, что выделяет адреса в точности с соответствующим RFC(занимает это регулярное выражение несколько страгниц). Но впрочем ниже будет подглава, посвященная чтению монстрообразных, на первый взгляд, регекспов налету, со множеством примеров, выше же мы уже попытались угадать предназначение регулярного выражения только по его виду.
Ключи, которые использовались в вышеприведенном регулярном выражении
g - глобальная замена
е - выполнение
x - улучшенное форматирование.
Если написать это регулярное выражение в одну строчку, то оно врядли там поместится:
s{((?:(?![-_])[\w-]+\.)+[A-Za-z][\w-])}#здесь силовой перевод каретки {"$1".(($addr=gethostbyname($1))?"[".inet_ntoa($addr)."]":"[???]")}gex
Разберем один интересный момент в данном регекспе:
s/regex/условие?да:иначе/
Тут проявляется пожалуй одна из действительно сильнейших особенностей regex, возможность в одном регулярном выражении избежать многострочных условий с циклом. В приведенном примере работает все примерно так: Если $addr=gethostbyname($1) - да, то ставить ip-адрес(inet_ntoa($addr)), если нет(не откликнулся сервер, сбой на линии и пр) то метить этот урл как подозрительный [???]. В принципе в программе ничего человеку делать не нужно, т.к. подозрительные отметаются условием print $file,"\n" if($file !~/\?\?\?/); Общее время работы программы 10-15 минут.
Обработка Форм
Ну вот ,вы уже знаете достаточно,кое в чем уже успели приобрести опыт, пришло время перейти к очень важной теме - обработке форм. При всей простоте (кажушейся) это едва ли не самое главное предназначение всего стандарта CGI . Куда бы вы не зашли на любой уважающий себя сайт,везде вы встретите формы, которые вам предложат заполнить.В этом деле можно положится только на CGI, так как Java и JavaScript ,выполняющиеся на страничке у клиента не имеют доступа к серверу,на котором находится сайт.
Коротко вспомним о том что происходит при рассматриваемом процессе поближе,так сказать на трезвую голову ;). Итак браузер требует у сервера определенный URL (это может быть как простой документ,так и сгенерированый CGI) в этом документе может содержаться форма.Отображая такой документ браузер также выводит элементы формы
(кнопки, поля ввода, поля ввода пароля, переключатели, радио-кнопки, списки, текстовые области,скрытые поля). И со всем этим добром пользователь может взаимодействовать.К форме естественно имеет доступ и встроеный язык программирования JavaScript -он может как использовать форму для своих нужд,не передавая CGI,так и помогать пользователю в заполнении формы.
После того,как пользователь заполнил форму он нажимат кнопку Submit которая говорит, что форму надо отправить на сервер. Браузер собирает все имена и значения элементов формы ,кодирует их методом urlencode
и в зависимости от указаного в тэге FORM метода вызывает GET или POST с указаным URL,передавая ему данные. На сервере CGI-скрипту это попадает (в зависимости от метода) либо в переменную QUERY_STRING
либо на STDIN.Скрипт может проверить данные ,занести их в какую нибудь базу данных,может как yahoo выполнить какой-нибудь поиск, может что-нибудь вычислить......да мало ли что он может,все зависит только от нашей фантазии..... В конце концов скрипт выдает браузеру ответ,который он и отображает.В этом ответе может содержаться все что вашей душе угодно от сообщения об удачном или неправильном запросе до таких ответов,по сравнению с которыми yahoo и altavista подвиснут от зависти, главное чтоб вам и тем кто посещает ваш сайт это нравилось.;)
Ну а теперь немного о синтаксисе элементов форм ,их описании и самое главное особенностях при обработке CGI-скриптом.
Итак немного экскурс в HTML:
FORM
<FORM action="http://......cgi" method="GET"|"POST" enctype="encodingType" name="formName" target="windowName" onSubmit="Handler"> </FORM>
Атрибуты:
action
как раз и задает тот URL,который будет и обрабатывать форму, если он опущен,то текущий URL документа(а он-то может быть сгенерирован нашим скриптом).
method
задает метод GET или POST
enctype
обычно не задается,для форм он application/x-www-form-urlencoded -по умолчанию, и поддерживается всеми CGI скриптами.Но если вы уж очень хотите чтобы браузер послал вам данные в другом формате (например text/plain) то можете указать этот тип кодировки,только потом не жалуйтесь,что ваш скрипт не может разделить поля,или вообще начинает глючить когда пользователь ввел какой-то спецсимвол.
name
Задается для JavaScript,чтоб обращатся к форме по имени,а не по номеру. Для CGI не играет ни какой роли,так как внутреннее для браузера.
target
Может Определять в какой фрейм отправить полученую информацию.Имеет значение во фреймосодержащих документах.Прозрачен для CGI обработки данных.
onSubmit
Определяет JavaScript -обработчик активизации формы.Применяется для проверки JavaScript'ом правильности заполнения.Опять таки прозрачен для CGI.
Пример типичной формы:
<FORM action="http://www.uic.nnov.ru/~paaa/cgi-bin/test.cgi" method="POST"> .........Поля формы......... </FORM>
Форма может содержать элементы.Элементы имеют имена,которые используются для кодирования пар имя=значение.Некоторые Элементы не передаются CGI,а используются JavaScript
для управления,например кнопки.Некоторые поля передаются только в тех случаях, когда в них что-то выбрано,например списки и переключатели.Остальные поля передаются всегда, даже когда они пустые.
Например:
<FORM action="http://www.doom/cgi-bin/test.cgi"> Your Name:<INPUT name="Name"><BR> E-Mail:<INPUT name="Email"><BR> Are you doomer:<INPUT type="checkbox" name="doomer" value="Yes"> <INPUT type="submit" value="Send Form!"> </FORM>
Допустим вы ввели имя lesha и адрес paaa@uic.nnov.ru, при этом выбрали переключатель После нажатия кнопки будет отправлен вот такой запрос:
http://www.doom/cgi-bin/test.cgi?Name=lesha&Email=paaa@uic.nnov.ru&doomer=Yes
Если же вы не выбрали переключатель,то запрос будет таким:
http://www.doom/cgi-bin/test.cgi?Name=lesha&Email=paaa@uic.nnov.ru
,как видите элемент doomer не вошел в строку запроса
Теперь попробуйте оставить поля редактирования пустыми:
http://www.doom/cgi-bin/test.cgi?Name=&Email=
Эти элементы (Name и Email) присутствуют и сообщают что они пустые.
Кнопка(button)
<INPUT type="button" name="buttname" value="Текст На Кнопке" onClick="Handler">
В форме изображается кнопка,при нажатии которой вызывается JavaScript-обработчик
заданый атрибутом onClick ,атрибут name служит для JavaScript-именования кнопки а не для передачи CGI.Так как значение кнопки не передается CGI, value задает Текст,изображаемый на кнопке.
<FORM onSubmit="return false;"> <INPUT type="button" value="Просто Кнопочка" onClick="alert('Нажали на кнопку!');"> </FORM> |
<INPUT type="submit" name="submitName" value="Отправить Форму" onClick="Handler">
Кнопка,предназначеная для передачи формы.Опять же,сама не передается,а служит только для управления. текст на ней задается атрибутом value.
<FORM onSubmit="alert('Нечего Посылать!');return false;"> <INPUT type="Submit" value="Послать!"> </FORM> |
<INPUT type="reset" name="resetName" value="Очистить" onClick="Handler">
Кнопка очистки формы.При ее нажатиивсем измененым элементам возвращается значение по умолчанию.
<FORM onSubmit="return false;"> <INPUT name="something"><BR> <INPUT type="reset" value="Очистить!"> </FORM> |
Поле ввода(text)
<INPUT [type="text"] name="textName" value="textValue" size=число [обработчики]>
Применяется очень часто,поэтому тип "text" служит для INPUT по умолчанию, его не надо каждый раз указывать.Имя поля,задаваемое name является обязательным для CGI
(в отличии от JavaScript,где элементы формы можно индексировать по номерам,а имена для удобства и читабельности кода служат).Можно задать значение по умолчанию атрибутом value,которое будет после загрузки докумета.атрибут size позволяет задать размер поля.Также может содержать обработчики onBlur,onChange,onFocus,onSelect.
<FORM onSubmit="return false;"> <INPUT name="something" size=30 value="Введите что-нибудь"> </FORM> |
<TEXTAREA name="textareaName" rows="число" cols="число" wrap="hard"|"soft"> TextToEdit </TEXTAREA>
Область многострочного редактирования.Размеры в строках и столбцах задаются атрибутами rows и cols.Значения атрибута wrap "hard" и "soft" -означают соответственно мягкую или жесткую разбивку на строки (в большинстве случаев ето не существенно). На что следует действительно обратить внимание так это на символ,используемый для указания перехода на новую строку. В Windows это '\r\n' а в Unix '\n',так что если это для вас существенно,то приводите преобразование,например так:
$my_text =~ s/\r\n/\n/g;
<FORM onSubmit="return false;"> <TEXTAREA name="MyText" rows=7 cols=30> Тут можно что-нибудь написать </TEXTAREA> </FORM> |
Тут можно что-нибудь написать |
<INPUT type="password" name="passName" size=число value="passValue">
Очень похоже на поле ввода,отличается тем что вместо символов в нем отображаются символы '*'.Служит для ввода пользователем пароля.
<FORM onSubmit="return false;"> Пароль: <INPUT type="password" name="yourpass" size=30> </FORM> |
Пароль: |
Скрытое поле(hidden)
<INPUT type="hidden" name="hiddName" value="hidValue">
Поле не отображаемое на экране.Но оно имеет имя и значение и следовательно передается в форму. Служит для того (и очень часто програмисты его применяют) чтоб передавать скрипту какую нибудь информацию.Например,если ваш скрипт обрабатывает несколько форм разных типов,то в скрытом поле каждой формы можно указать с какой формой конкретно вы имеете дело. Так как это ваша внутренняя кухня то нечего пользователю мозолить глаза этой информацией.
<FORM onSubmit="return false;"> Этого сдесь вам не видно,поле-скрытое. <INPUT type="hidden" name="formNum" value="3"> </FORM> |
Этого сдесь вам не видно,поле-скрытое. |
<INPUT type="checkbox" name="checkboxname" value="checkboxValue" [checked] onClick="Handler">Text
В отличии от кнопки,атрибут value сдесь не задает на надпись на переключателе,а его значение(внутреннее).Поэтому если надо что-то подписать,пишите рядом в ним. Может быть сразу выбраным если указан атрибут checked .Если value не указано то значение по умолчанию "on" .Передается только в том случае,когда выбран.
<FORM onSubmit="return false;"> <INPUT type="checkbox" name="inet" value="Yes" checked>Доступ к Интернет </FORM> |
Доступ к Интернет |
<INPUT type="radio" name="radioName" value="radioVal1" [checked] onClick="Handler">Text
В отличие от checkbox может быть несколько радиокнопок с одинаковым параметром name ,но с разными value,из них передается только та,что выбрана.Одна из них может быть изначально выбрана по умолчанию checked.Например:
<FORM onSubmit="return false;"> Вы уверены?<BR> <INPUT type="radio" name="Radbut" checked>Yes <INPUT type="radio" name="Radbut">No </FORM> |
Вы уверены? Yes No |
Список(select)
<SELECT name="SelectName" size=число [multiple] [обработчики] > <OPTION value="optionValue1" [selected]>Опция 1 <OPTION value="optionValue2" [selected]>Опция 2 <OPTION value="optionValue3" [selected]>Опция 3 ..... <OPTION value="optionValueN" [selected]>Опция N </SELECT>
Задает список,позволяющий выбрать одну ( или несколько) опций из списка. Если атрибут multiple не указан,то создается простой выпадающий список,в котором можно выбрать только одну из опций.Его значение всегда передается,т.к. всегда хоть одно выбрано. Если указан атрибут multiple,то во первых можно указать размер видимой части списка атрибутом size (Если опций больше появится скролинг).Во вторых передаются только выбраные опции ,т.е.Он может передатся несколько раз ?SelectName=opt1&SelectName=opt2&SelectName=opt9
если выбраны скажем несколько опций.А может и не разу,если ничего не выбрано из списка. Можно задавать обработчики onBlur,onChange,onFocus.
<FORM onSubmit="return false;"> Ваш цвет:<BR> <SELECT name="singleSel"> <OPTION value="white">Белый <OPTION value="black">Черный <OPTION value="magenta">Фиолетовый <OPTION value="green">Зеленый <OPTION value="red">Красный </FORM> |
Ваш цвет: Белый Черный Фиолетовый Зеленый Красный |
<FORM onSubmit="return false;"> Какие сорта пива вы пили:<BR> <SELECT name="miltiSel" multiple size=4> <OPTION value="Балтика">Балтика <OPTION value="Толстяк">Толстяк <OPTION value="Премьер">Премьер <OPTION value="Хольстен">Хольстен <OPTION value="Бавария">Бавария <OPTION value="Coca-Cola ;)">Coca-Cola ;) </SELECT> </FORM> |
Какие сорта пива вы пили: Балтика Толстяк Премьер Хольстен Бавария Coca-Cola ;) |
Небольшая Помощь JavaScript
Для CGI-програмиста конечно JavaScript -это иной мир, вы можете спокойно пропустить этот абзац,если вы не знаете JavaScript,так как написаное в нем к CGI не относится, а скорей к самим формам и дизайну сайта.Я скажу пару слов о том как JavaScript может оказать посильную помощь в проверке правильности заполнения форм.Все проверки конечно можно и нужно делать на сервере,но когда имеешь дело с рассеяным пользователем, то для него заполнение простой формы превратится в мучение.Поясню на примере,в форме есть какие-то обязательные поля,например имя.Если пользователь забыл его указать то скрипт скажет ему об этом в сообщении он исправит это, допустим что-нибудь еще не так ввел .... Только на передачу данных по сети
может уходить масса времени.А на обработку на локальной машине-доли секуды.
Вот Например как это можно применить JavaScript для предварительного контроля правильности. Допустим простейшая форма содержит имя и возраст.Имя не должно быть пустым, а возраст должен состоять из цифр.
<HTML><HEAD> <SCRIPT language="JavaScript"> <!-- function IsNumber(data){ var NumStr="0123456789"; var ch;var count; for(var i=0;i<data.length;i++){ ch=data.substring(i,i+1); if(NumStr.indexOf(ch)!=-1)count++; } if(counter==data.length)return true; else return false; } function IsEmpty(data){ if(data.length==0)return true; else return false; } function IsFormOk(f){ if(IsEmpty(f.Name.value)){ alert('Имя не должно быть пустой строкой'); return false; } if(!IsNumber(f.Age.value)){ alert('Возраст должен состоять из цифр'); return false; } return true; } //--></SCRIPT></HEAD> <BODY> <FORM action="http://www.test.ru/cgi-bin/test.cgi" onSubmit="IsFormOk(this.form)"> Your Name:<INPUT name="Name"><BR> Your age:<INPUT name="Age"><BR> <INPUT type="submit" value="Послать Данные"> </FORM> </BODY></HTML>
Ну вот , на этом можно закончить это краткое введение в HTMLые формы.
Итак,У нас на входе скрипта данные формы,закодированые методом urlencode
Положеные в Переменную QUERY_STRING или подаваемые на STDIN.Мы должны вопервых их получить.
if($ENV{'REQUEST_METHOD'} eq 'GET'){#Анализируем метод,GET или POST $query=$ENV{'QUERY_STRING'}; } elsif($ENV{'REQUEST_METHOD'} eq 'POST'){ sysread STDIN,$query,$ENV{'CONTENT_LENGTH'}; }
Вот,мы уже считали наш запрос в переменную $query.Теперь пришло самое время ее обработать. Мы знаем что поля разделены символом '&' значит используем его в качестве разделителя функции split:
@formfields=split /&/,$query;
Вот разделили,а теперь организуем цикл foreach по полученым полям @formfields
foreach(@formfields){ if(/^Name=(.*)/){$name=urldecode($1);} if(/^Age=(.*)/){$age=urldecode($1);} }
Сдесь выражение в регулярном выражении в круглых скобках (.*) после знака '=',запоминается в скалярную переменную $1 ,которая затем и декодируется нашей старой и знакомой функцией urldecode (я предупреждал,что она будет почти в каждой вашей CGI-программе)
sub urldecode{ #очень полезная функция декодирования local($val)=@_; #запроса,будет почти в каждой вашей CGI-программе $val=~s/\+/ /g; $val=~s/%([0-9A-H]{2})/pack('C',hex($1))/ge; return $val; }
Так мы проходим по всем полям,которые нам переданы.Это стандартный подход,он годится в качестве шаблона.У вас может возникнуть вопрос,а что делать если вам переданы данные от списка у которого задана возможность выбора нескольких элементов и данные поступают в таком виде: Sel=opt1&Sel=opt2&Sel=opt9. Тут тоже нет никаких проблем,просто запихиваем эти поступающие значения в массив.
foreach(@formfields){ ..... if(/^Sel=(.*)/){push @Sel,urldecode($1);} ..... }
И потом спокойно оперируем с Полученым Массивом @Sel.
На этом можно так сказать заканчивается шаблонная часть скрипта и начинается содержательная, которая зависит только от вашей фантазии.....
Вы можете сколько угодно анализировать полученые значения,обращатся при этом к различным файлам .Если вы к этому приложите фантазию,то кто знает что получится....
А Пока Ради примера я вам напишу скрипт, который ведет социологическое исследование насчет курения и отношения к нему.Может он слишком массивен для данного пособия, но зато он наглядно показывает как достаточно простыми средствами можно проводить социологические исследования.
<HTML><!-- HTML файл с формой,можете повесить его себе на сайт! -> <HEAD><TITLE>Социологический опрос насчет курения</TITLE></HEAD> <BODY> <CENTER><H1>Социологический опрос насчет курения</H1></CENTER> <FORM action="cgi-bin/smoketest.cgi"> <TABLE> <TR><TD>Ваш возраст:</TD><TD><INPUT name="age"></TD></TR> <TR><TD>Вы курите(Y/N):</TD> <TD><INPUT type="radio" name="smoke" value="Yes" checked>Да <INPUT type="radio" name="smoke" value="No">Нет</TD></TR> <TR><TD>Как вы относитесь если рядом кто-то курит?</TD> <TD><SELECT name="sm_near"> <OPTION value="0">Резко негативно <OPTION value="1">Негативно <OPTION value="2" selected>Мне все равно <OPTION value="3">Позитивно <OPTION value="4">Резко позитивно </SELECT> </TD></TR> <TR><TD>Сколько вы выкуриваете в день?</TD> <TD><SELECT name="sm_day"> <OPTION value="0">Ни сколько <OPTION value="1">1 сигарету <OPTION value="2">2 сигареты <OPTION value="5">около 5 <OPTION value="0.5pac">полпачки <OPTION value="pac">пачку <OPTION value="2pac">2 пачки <OPTION value="more">больше </SELECT> </TD></TR> <TR><TD>Как давно вы начали курить?</TD> <TD><SELECT name="sm_stage"> <OPTION value="noatall">Не начинал <OPTION value="onetime">Бросил <OPTION value="0.5year">Полгода <OPTION value="1year">Год <OPTION value="2year">2 Года <OPTION value="5year">5 Лет <OPTION value="more">Больше </SELECT> </TD></TR> <TR><TD>Считаете ли вы это опасным для своего здоровья?</TD> <TD><SELECT name="sm_danger"> <OPTION value="0">Очень Опасно <OPTION value="1">Думаю,что да <OPTION value="2" selected>Не знаю <OPTION value="3">Может самую малость <OPTION value="4">Нет,Безопасно. </SELECT> </TD></TR> <TR><TD>Хотите ли вы бросить?</TD> <TD><SELECT name="sm_nosmoke"> <OPTION value="0">Уже бросаю <OPTION value="1">Думаю бросить <OPTION value="2" selected>Иногда <OPTION value="3">Очень Редко <OPTION value="4">Никогда. </SELECT> </TD></TR> <TR><TD><INPUT type="submit" value="Послать Данные"></TD> <TD><INPUT type="reset" value="Очистить Форму"></TD></TR> </TABLE> </FORM> </BODY></HTML>
А вот скрипт для его обработки:
#!/usr/bin/perl #smoketest.cgi $datafile="smoke.dat"; sub urldecode{ local($val)=@_; $val=~s/\+/ /g; $val=~s/%([0-9A-H]{2})/pack('C',hex($1))/ge; return $val; } sub print_err{ print "Content-Type: text/html\n\n"; print "<HTML><HEAD><TITLE>Error!!</TITLE></HEAD>"; print "<BODY><CENTER><H1>@_</H1>"; print "</BODY></HTML>"; exit; } if($ENV{'REQUEST_METHOD'} eq 'GET'){$query=$ENV{'QUERY_STRING'};} elsif($ENV{'REQUEST_METHOD'} eq 'POST') {sysread STDIN,$query,$ENV{'CONTENT_LENGTH'};} if($query ne ''){ @formfields=split /&/,$query; foreach(@formfields){ if(/^age=(.*)/){$age=urldecode($1);} if(/^smoke=(.*)/){$smoke=urldecode($1);} if(/^sm_near=(.*)/){$sm_near=urldecode($1);} if(/^sm_day=(.*)/){$sm_day=urldecode($1);} if(/^sm_stage=(.*)/){$sm_stage=urldecode($1);} if(/^sm_danger=(.*)/){$sm_danger=urldecode($1);} if(/^sm_nosmoke=(.*)/){$sm_nosmoke=urldecode($1);} } if((!$age)($age=~/\D/)){ print "Content-Type: text/html\n\n"; print "<HTML><BODY><H2>Возраст введен неправильно,должен состоять из цифр.</H2>"; print "<FORM><INPUT type=\"button\" value=\"Вернуться назад к Анкете\""; print "onClick=\"history.back();\"></FORM>"; print "</BODY></HTML>"; } $anket_str=join '\t',($age,$smoke,$sm_near,$sm_day,$sm_stage,$sm_danger,$sm_nosmoke); open DATA,">>$datafile" print_err("Cannot open $datafile $!"); print DATA "$anket_str\n"; close DATA; } open DATA,"$datafile" print_err("Cannot open $datafile $!"); @AllData=<DATA>; close DATA; $total=$#AllData; foreach(@AllData){ ($age,$smoke,$sm_near,$sm_day,$sm_stage,$sm_danger,$sm_nosmoke)=split /\t/,$_; $smok_total++ if ($smoke eq 'Yes'); $nosmok_total++ if ($smoke eq 'No'); if($age<16){$age16_total++; if($smoke eq 'Yes'){$age16_sm++;}else{$age16_nosm++;} } if(($age>16)&&($age<=18)){$age16_18_total++; if($smoke eq 'Yes'){$age16_18_sm++;}else{$age16_18_nosm++;} } if(($age>18)&&($age<=20)){$age18_20_total++; if($smoke eq 'Yes'){$age18_20_sm++;}else{$age18_20_nosm++;} } if($age>20){$age20_total++; if($smoke eq 'Yes'){$age20_sm++;}else{$age20_nosm++;} } if($sm_near eq '0'){$near0++;} if($sm_near eq '1'){$near1++;} if($sm_near eq '2'){$near2++;} if($sm_near eq '3'){$near3++;} if($sm_near eq '4'){$near4++;} if($sm_day eq '0'){$day0++;} if($sm_day eq '1'){$day1++;} if($sm_day eq '2'){$day2++;} if($sm_day eq '5'){$day5++;} if($sm_day eq '0.5pac'){$dayhalfpac++;} if($sm_day eq 'pac'){$daypac++;} if($sm_day eq '2pac'){$day2pac++;} if($sm_day eq 'more'){$daymore++;} if($sm_stage eq 'noatall'){$stagenoatall++;} if($sm_stage eq 'onetime'){$statgeonetime++;} if($sm_stage eq '0.5year'){$stagehalfyear++;} if($sm_stage eq '1year'){$stage1year++;} if($sm_stage eq '2year'){$stage2year++;} if($sm_stage eq '5year'){$stage5year++;} if($sm_stage eq 'more'){$stagemore++;} if($sm_danger eq '0'){$danger0++;} if($sm_danger eq '1'){$danger1++;} if($sm_danger eq '2'){$danger2++;} if($sm_danger eq '3'){$danger3++;} if($sm_danger eq '4'){$danger4++;} if($sm_nosmoke eq '0'){$stopsmoke0++;} if($sm_nosmoke eq '1'){$stopsmoke1++;} if($sm_nosmoke eq '2'){$stopsmoke2++;} if($sm_nosmoke eq '3'){$stopsmoke3++;} if($sm_nosmoke eq '4'){$stopsmoke4++;} } ######### print "Content-Type: text/html\n\n"; print "<HTML><HEAD><TITLE>Результаты обработки данных</TITLE></HEAD>"; print "<BODY bgcolor=\"yellow\">"; unless($total){print "<H1>Еще нет данных</H1></BODY></HTML>";exit;} print "<CENTER><H1>Результаты обработки данных</H1></CENTER>"; print "<BR>\n"; print "Обработано анкет: $total<BR>\n"; print "Общие данные Всего:<BR>\n"; print "Курящие:$smok_total (".(($smok_total/$total)*100) ."%)<BR>\n"; print "Некурящие:$nosmok_total (".(($nosmok_total/$total)*100)."%)<BR>\n"; print "<TABLE>\n"; print "<TR><TD colspan=4>Возрастные группы:(<16,16..18,18..20,>20)</TD></TR>\n"; print "<TR><TD>Возраст</TD><TD>Курящие</TD><TD>Некурящие</TD><TD>Всего</TD></TR>\n"; print "<TR><TD><16:</TD><TD>$age16_sm</TD><TD>$age16_nosm</TD><TD>$age16_total</TD></TR>\n"; print "<TR><TD>16..18:</TD><TD>$age16_18_sm</TD><TD>$age16_18_nosm</TD><TD>$age16_18_total</TD></TR>\n"; print "<TR><TD>18..20:</TD><TD>$age18_20_sm</TD><TD>$age18_20_nosm</TD><TD>$age18_20_total</TD></TR>\n"; print "<TR><TD>>20:</TD><TD>$age20_sm</TD><TD>$age20_nosm</TD><TD>$age20_total</TD></TR>"; print "</TABLE>\n"; print "<TABLE>\n"; print "<TR><TD colspan=2>Отношение когда кто-то курит рядом:(%)</TD></TR>\n"; print "<TR><TD>Резко негативно</TD><TD>".(($near0/$total)*100)."</TD></TR>\n"; print "<TR><TD>Негативно </TD><TD>".(($near1/$total)*100)."</TD></TR>\n"; print "<TR><TD>Мне все равно </TD><TD>".(($near2/$total)*100)."</TD></TR>\n"; print "<TR><TD>Позитивно </TD><TD>".(($near3/$total)*100)."</TD></TR>\n"; print "<TR><TD>Резко позитивно</TD><TD>".(($near4/$total)*100)."</TD></TR>\n"; print "</TABLE>\n"; print "<TABLE>\n"; print "<TR><TD colspan=2>В среднем выкуривают:(%)</TD></TR>\n"; print "<TR><TD>не курят: </TD><TD>".(($day0/$total)*100)."</TD></TR>\n"; print "<TR><TD>1 сигарету:</TD><TD>".(($day1/$total)*100)."</TD></TR>\n"; print "<TR><TD>2 сигареты:</TD><TD>".(($day2/$total)*100)."</TD></TR>\n"; print "<TR><TD>5 сигарет: </TD><TD>".(($day5/$total)*100)."</TD></TR>\n"; print "<TR><TD>полпачки: </TD><TD>".(($dayhalfpac/$total)*100)."</TD></TR>\n"; print "<TR><TD>пачку: </TD><TD>".(($daypac/$total)*100)."</TD></TR>\n"; print "<TR><TD>2 пачки: </TD><TD>".(($day2pac/$total)*100)."</TD></TR>\n"; print "<TR><TD>больше: </TD><TD>".(($daymore/$total)*100)."</TD></TR>\n"; print "</TABLE>\n"; print "<TABLE>\n"; print "<TR><TD colspan=2>Стаж курения:(%)</TD></TR>\n"; print "<TR><TD>Не начинал</TD><TD>".(($stagenoatall /$total)*100)."</TD></TR>\n"; print "<TR><TD>Бросил </TD><TD>".(($statgeonetime/$total)*100)."</TD></TR>\n"; print "<TR><TD>Полгода </TD><TD>".(($stagehalfyear/$total)*100)."</TD></TR>\n"; print "<TR><TD>Год </TD><TD>".(($stage1year /$total)*100)."</TD></TR>\n"; print "<TR><TD>2 Года </TD><TD>".(($stage2year /$total)*100)."</TD></TR>\n"; print "<TR><TD>5 Лет </TD><TD>".(($stage5year /$total)*100)."</TD></TR>\n"; print "<TR><TD>Больше </TD><TD>".(($stagemore /$total)*100)."</TD></TR>\n"; print "</TABLE>\n"; print "<TABLE>\n"; print "<TR><TD colspan=2>Курение опасно:(%)</TD></TR>\n"; print "<TR><TD>Очень Опасно </TD><TD>".(($danger0/$total)*100)."</TD></TR>\n"; print "<TR><TD>Думаю,что да </TD><TD>".(($danger1/$total)*100)."</TD></TR>\n"; print "<TR><TD>Не знаю </TD><TD>".(($danger2/$total)*100)."</TD></TR>\n"; print "<TR><TD>Может самую малость</TD><TD>".(($danger3/$total)*100)."</TD></TR>\n"; print "<TR><TD>Нет,Безопасно. </TD><TD>".(($danger4/$total)*100)."</TD></TR>\n"; print "</TABLE>\n"; print "<TABLE>\n"; print "<TR><TD colspan=2>Хотели ли вы бросить:(%)</TD></TR>\n"; print "<TR><TD>Уже бросаю </TD><TD>".(($stopsmoke0/$total)*100)."</TD></TR>\n"; print "<TR><TD>Думаю бросить</TD><TD>".(($stopsmoke1/$total)*100)."</TD></TR>\n"; print "<TR><TD>Иногда </TD><TD>".(($stopsmoke2/$total)*100)."</TD></TR>\n"; print "<TR><TD>Очень Редко </TD><TD>".(($stopsmoke3/$total)*100)."</TD></TR>\n"; print "<TR><TD>Никогда. </TD><TD>".(($stopsmoke4/$total)*100)."</TD></TR>\n"; print "</TABLE>\n"; print "</BODY></HTML>";
Общие вопросы
Q: Что такое Perl?
A: Practical Extraction and Report Language. Язык программирования, который
задумывался, как удобный язык для работы со строками, и не стесненный, к тому
же, ограничениями, свойственными другим языкам. Фактически, получился удобный
язык, на котором программы быстро пишутся (в общем случае это не значит, что
они работают быстрее, например, программ С++ - но написать код можно точно
быстрее)
Q: Кто его придумал?
A: Larry Wall. А развивается он при помощи тысяч энтузиастов по всему миру.
Q: Для чего его используют?
A: Самое распространенное использование - в качестве CGI-программ,
выполняемых на интернет-сайтах.
Q: Под какие платформы он существует?
A: perl 5 - под практически все виды UNIX-систем (Linux, FreeBSD, SunOS,..),
DOS (защищенный режим, i386 и выше), Windows 9x/NT, OS/2, VAX VMS, Novell
Netware. Amiga..
Q: Сайт перла какой? Где взять последнюю версию?
A: www.perl.com ; для windows - www.activestate.com
Q: А компилятор где взять?
A: (платный)
Q: Как сделать то-то? Может, уже есть готовая библиотека?
A:
CPAN (Comprehensive Perl Archive Network) - огромное, отсортированное собрание всех дополнительных модулей к perl,
не включенных в поставку. Собраны все версии модулей. Рядом с ними лежат
readme. Скачиваете, и устанавливаете. В 90% случаев то, что вы пытаетесь
сделать, уже кто-то сделал и выложил на CPAN.
Q: Где взять скриптов готовых, побольше?
A: www.cgi-resources.com
www.cgiresources.com
www.script.ru
www.codemanual.com
Только не используйте и не разбирайте скрипты из Matt's scripts archive. Он не умеет правильно их писать, лучше не учиться плохому.
Q: А вот у меня вопрос...
A: Во-первых, вместе с perl поставляется уйма документации. Hачиная с perlfaq,
который HЕОБХОДИМО прочесть (хотя бы просмотреть заголовки). Кроме этого,
также вместе с ним поставляется программа perldoc:
perldoc - описание для установленного модуля,
perldoc -f - описание функции,
perldoc -q - поиск по ФАК'у:
perldoc perldoc - тоже интересно
В большинстве статей документации есть примеры кода.
Если с вашим дистрибутивом perl не поставляется perldoc - выкиньте этот дистрибутив.
Что еще можно найти в perldoc:
perl | О перле вообще |
perldelta | Что нового в последней версии перла |
perlfaq | FAQ |
perltoc | Подробное оглавление ко всей документации |
perldata | Типы данных |
perlsyn | Синтаксис языка |
perlop | Операции перл |
perlre | Регулярные выражения |
perlrun | Опции командной строки |
perlfunc | Встроенные функции |
perlvar | Специальные переменные |
perlsub | Функции (процедуры) |
perlmod | Модули |
perlmodlib | Модули: создание собственных библиотек |
perlmodinstall | Установка модулей CPAN |
perlform | Форматы |
perllocale | Локализация |
perlref | Ccылки и указатели на данные |
perldsc | Введение в структурные типы данных |
perllol | Структуры данных: массивы и списки |
perltoot | Тьюториал по объектно-ориентированному программированию |
perlobj | Объекты в перле |
perltie | Связь объектов с обыкновенными переменными |
perlbot | Perl OO tricks and examples |
perlipc | Связь между процессами |
perldebug | Отладка программ |
perldiag | Сообщения об ошибках |
perlsec | Вопросы безопасности |
perlembed | Способы внедрения перл-программ в программы на C/C++ |
perlapio | Собственный API, использумый в исходниках перла |
perlxs | XS -- программирование перловских библиотек, используемых вместе с библиотеками на C |
perlxstut | Учебник по XS |
perlguts | Внутренние функции перла для разработчиков |
perlcall | Соглашения о вызове перловских функций из C |
perlhist | История и полный список всех версий перла |
perlbook | Книги |
lwpcook | "Поваренная книга" LWP |
Q: Какие существуют редакторы для написания программ, с подсветкой?
A: Мультиплатформенный - vim, emacs, xemacs
Под win32 - FAR с плагином Colorer, perl scripting tool
EditPlus и множество других
Очень простое решение для зеркала новостной ленты
Допустим нужно сделать зеркало какой-либо зарубежной новостной ленты вместе с загрузкой картинок с удаленного сервера, чтобы не ждать по несколько минут отображения содержимого полностью загруженной большой таблицы. Приведенный скрипт запускается при помощи crontab каждые 5 часов:
#!/usr/bin/perl -w $/="\001"; print "content-type: text/html\n\n"; $dir="/var/www/docs/html/news/images"; $imgurl="http://www.qwerty.ru/news/images"; use LWP::Simple; use LWP::UserAgent; $page=get "http://www.astronomynow.com"; $page=~s/face="(.*?)"//igs; &getimg($page); $page=~s!/images/grafix/listdot.gif!../../listdot.gif!igs; $page=~s!/images/grafix/spacer.gif!../../spacer.gif!igs; $page=~s!images/grafix/spacer.gif!../../spacer.gif!igs; if($page=~m!<TABLE WIDTH="400" BORDER="0" CELLPADDING="0" CELLSPACING="0">(.*?)</TD></TR></TABLE>!igsm){ $file=$1; &getlink($page); foreach $names(@res){ $names=~s|.*/ig; $file=~s|src="http://(.*?)$names"|src=$imgurl/$names|igs; } $html=qq~ <TABLE BORDER="0" CELLPADDING="0" CELLSPACING="0"> $file </TD></TR></TABLE>~; } open F, ">$dir/news.txt"; print F $html or die "\n\n\n ERROR: $!\n\n\n"; close F; sub getimg{ &getlink($_[0]); foreach $img(@res){ my $res = LWP::UserAgent->new->request(new HTTP::Request GET => $img); if ($res->is_success) { $img=~s|.*/; open (ABC, ">$dir/$img") or die "\n\n\nERROR: $!\n\n\n"; binmode(ABC); print ABC $res->content; close ABC or die "\n\n\nERROR: $!\n\n\n"; } else { print $res->status_line; } } return @res; } sub getlink{ local $_=$_[0]; push(@res, "http://$2") while m{SRC\s*=\s*(["'])http://(.*?)\1\s*(.*?)WIDTH="100" HEIGHT="100"(.*?)>}igs; return @res; }
Одиночные символы
В регулярном выражении любой символ соответствует самому себе, если только он не является метасимволом со специальным значением (такими метасимволами являются \, |, (, ), [, {, *, +, ^, $, ? и .). В следующем примере проверяется, не ввел ли пользователь команду "quit" (и если это так, то прекращаем работу программы):
while(<>){ if(m/quit/){exit;} }
Правильнее проверить, что введенное пользователем слово "quit" не имеет со-седних слов, изменяющих смысл предложения. (Например, программа выполнит заведомо неверное действие, если вместо "quit" пользователь введет команду "Don't quit!".) Это можно сделать с помощью метасимволов ^ и $. Заодно, что-бы сравнение было нечувствительно к разнице между прописными и заглавными буквами, используем модификатор i:
while (<>) {if (m/^quit$/i) {exit;} }
Кроме обычных символов perl определяет специальные символы. Они вводятся с помощью обратной косой черты (escape-последовательности) и также могут встречаться в регулярном выражении:
\077 - восьмеричный символ,
\а - символ BEL (звонок),
\с[ - управляющие символы (комбинация Ctrl + символ, в данном случае это управляющий символ ESC),
\d - соответствует цифре,
\D - соответствует любому символу, кроме цифры,
\е - символ escape (ESC),
\Е - конец действия команд \L, \U и \Q,
\f - символ прогона страницы (FF),
\1 - следующая литера становится строчной (lowercase),
\L - все последующие литеры становятся строчными вплоть до командй \Е,
\n - символ новой строки (LF, NL),
\Q - вплоть до команды \Е все последующие метасимволы становятся обычными символами,
\r - символ перевода каретки (CR),
\s - соответствует любому из "пробельных символов" (пробел, вертикальная , или горизонтальная табуляция, символ новой строки и т. д.),
\S - любой символ, кроме "пробельного",
\t - символ горизонтальной табуляции (НТ, TAB),
\u - следующая литера становится заглавной (uppercase),
\U - все последующие литеры становятся заглавными вплоть до команды \E,
\v - символ вертикальной табуляции (VT),
\w - алфавитно-цифровой символ ( любая буква, цифра или символ подчеркивания),
\W - любой символ, кроме букв, цифр и символа подчеркивания,
\x1B - шестнадцатиричный символ.
Bat также можете "защитить" любой метасимвол, то есть заставить perl рассматривать его как обыкновенный символ, а не как команду, поставив перед метасимволом обратную косую черту \. Обратите внимание на символы типа \w, \d и \s, которые соответствуют не одному, а любому символу из некоторой группы. Также заметьте, что один такой символ, указанный в шаблоне, соответствует ровно одному символу проверяемой строки. Поэтому для задания шаблона, соответствующего, например, слову из букв, цифр и символов подчеркивания, надо использовать конструкцию \w+, как это сделано в следующем примере:
$text = "Here is some text." $text =~ s/\w+/There/; print $text; There is some text.
Определения
Регулярные выражения в perl одна из самых мощных его возможностей. regex позволяют в perl сопоставлять текст с указанным шаблоном, разбивать текст в массив по шаблону, производить замену текста по шаблону и многое многое другое. Так-же иногда регекспами называются операторы поиска и замены.
Оператор q(text) заменяет строку text на строку, заключенную в одинарные кавычки(например если в q(text) поставить символ q(text\n), то напечатает text\n , т.е. \n это два символа, подобно print 'amam $file' напечатает amam $file). В данном случае почти все специальные символы не будут интерпретироваться внутри q(), исключая '\'
$some=q(Don't may be);
Оператор qq~text~; (вместо значка ~ можно ставить например знак |) позволяет работать со строками и многострочными текстами. пользуясь этим оператором можно выводить целые куски html-кода и писать в этом коде имена скалярных переменных.
Оператор qw("text") разбивает строку на массив слов.
@mass=qw("я вышел погулять и увидел как через реку строят новый мост"); #хотя с настроенной локалью будет работать и @mass=qw(я вышел погулять и увидел как через реку строят новый мост); for(@mass){print $_,"\n"}
Оператор qr/pattern/ ключи - imosx
работает подобно регулярному выражению s/.../.../
$rex=qr/my.STRING/is; s#$rex#foo#; #тоже самое, что и s/my.STRING/foo/is;
Результат может использоваться подобно вызову подпрограммы(см perldoc perlop Regex quote like operator)
$re=qr/$pattern/; $string=~/foo${re}bar/; $string=~$re; $string=~/$re/;
Ключи imosx стандартные(см. ниже)
Оператор qx/STRING/ работает как системная команда, подобно $output = `cmd 2>$1`;. Программа, иллюстрирующая использование данного оператора:
#!/usr/bin/perl qx[dbfdump --fs="\x18" --rs="\x19" pdffile.dbf >pdffile.txt];
файл pdffile.dbf содержит memo-поля(memo-поле содержит ссылку, подобно функции seek, на текст в файле с расширением *.fpt), которые при помощи DBI.pm мне когда-то давно выудить не удалось. Принимает разрешения FoxBASE4 и дампит файлы со встроенными memo-полями в текстовый вид. Т.е. таким образом получилось вытащить информацию из файла memo-типа *.fpt.
Допустим используя команду $perl_info = qx(ps $$); мы выводим информацию о текущем процессе запущенного скрипта(каждая запущенная программа в UNIX имеет свой собственный уникальный идентификатор, который содержится во встроенной переменной $$ - достаточно уникальное число, можно использовать почти как счетчик случайных чисел). Если сказать $shell_info = qx'ps $$'; то выведет информацию о самом ps. Т.е. скобки осуществляют своеобразное экранирование от двойной кавычки.
В перл есть три основных оператора, работающих со строками:
m/.../ - проверка совпадений (matching),
s/.../.../ - подстановка текста (substitution),
tr/.../ - замена текста (translation).
Опертаор m/.../ анализирует входной текст и ищет в нем подстроку совпадающую с указанным шаблоном (он задан регулярным выражением). Оператор s/.../.../ выполняет подстановку одних текстовых фрагментов вместо других, при помощи регулярных выражений. Оператор tr/.../.../ заменяет выходной текст, но при этом он не использует регулярные выражения, осуществляя замену посимвольно.
Оператор m/шаблон/ - поиск подстроки по определенному шаблону. Например print "$1 г.\n" while m!((\d){4})!g найдет и выведет все даты в переменной $_. В шаблоне не важно, что будет его ограничителем. Например при поиске гиперссылок, которые зачастую содержат символы /, разумнее пользоваться не /, а например # или ! как символами ограничителями. В таком случае шаблон будет более прост для понимания другим программистам, да и немного короче. В perl оператор m/.../ используется очень часто, и поэтому используется сокращение, без начальной буквы m. Если начальная буква есть, то в качетсве символов ограничителей можно исползовать любой другой символ.
Для оператора m/pattern/ есть 6 параметров: gimsxo
m/foo/g говорит компилятору найти все foo в тексте, в то время как m/foo/ найдет только первое вхождение подстроки foo в строке $_. В строке $_ содержится обычный текст, как и в переменной $text$, $_ такая-же переменная, только она существует всегда и вводится, когда не определена специально другая, по умолчанию.
Например можно сказать for (@mass){print $_,"\n"} или for $elem (@mass){print $elem,"\n"}. Эти две строчки делают одно и то-же, но в первом случае запись короче, да и зачастую бывает удобно использовать переменную $_, например, когда нужно выделить при помощи регулярного выражения определенные данные, пользуясь перебором массива(функция map):
@res=map{/(\d\d\d\d)/} split /\s/, $texts;
что эквививалентно коду
push @res, $1 while m!((\d){4})!g; #(в данном случае $_=$texts)
или что эквивалентно конструкции
foreach(split /\s/, $texts){ push @res, $1 if(/(\d\d\d\d)/g) }
Следующий параметр m/foo/i, говорит о том, что не нужно учитывать регистр при поиске по подстроке.
Параметр m/foo/s говорит от том, что строка, по которой производится поиск, состоит из одной строчки.
Например нужно выцепить все url картинок из странички www.astronomynow.com, чтобы сделать локальное зеркало этой странички и пользователи могли с интересом читать последние новости астрономии:
#!/usr/bin/perl -wT use LWP::Simple; $page=get "http://www.astronomynow.com"; &getlink($page); sub getlink{ local $_=$_[0]; push(@res, "http://$2") while m{SRC\s*=\s*(["'])http://(.*?)\1\s*(.*?)WIDTH="100" HEIGHT="100"(.*?)>}igs }
В подпрограмме заводится при помощи функции local переменная, видимая только в области действия подпрограммы. Этой переменной присваивается значение переменной $page, в которой содержится текст выкачанной Simple.pm странички.
Можно сделать немного по другому, сохранить скачанную страничку в файл на диск и затем следующее:
$/="\001"; open F, "<page.html"; $page=<F>; close F; &getlink($page); ...
Встроенная переменная $/ содержит символ разделителя входных записей. Это может быть перевод каретки или, при upload far'ом на сервер файлов в не ASCI виде, она приобретают на конце строчки хитрый символ ^M.
Если $/ переопределить, то можно свободно пользоваться дескрипторами открытия файлов для просмотра многострочного текста(m/pattern/s). Например когда открывается файл при помощи функции open F, "<file.txt"; @mass=<F>, то присваивая дескриптор F массиву в массиве появятся строчки, разделенные символом, содержащимся в $/.
Переопределив $/ можно запросто написать:
open F, "<file.txt"; $mass=<F>
и в переменной $ mass будет содержаться многострочный текст с точки зрения человека, но программа будет видеть этот текст как одну строку и по тексту можно будет запросто пройтись поиском m/pattern/igs и выделить все необходимые подстроки.
Параметр m/foo/o говорит от том, что шаблон нужно компилировать только один раз. Если оператор используется в сочетании с операциями привязки =~ и отрицание !~, то строкой, в которой ведется поиск, является переменная, стоящая слева от операции привязки. В противном случае поиск ведется в строке $_.
Оператор s!pattern!substring! - поиск в строке по шаблону pattern и замена найденного текста на substring. Как и для оператора m/.../, косую черту можно не ставить, пригоден любой символ, который не находится в противореции с заданным выражением. Не рекомендуется использовать в качестве ограничителей ? и '.
s!/usr/local/etc/!/some/where/else! - заменяет путь.
s(/usr/local/etc/)(/some/where/else)g - заменяет все встречающимеся пути до файла.
параметры: egimsxo
e - указывает, что substring нужно вычислить.
например нужно переделать все escape последовательности, для этого вызывается соответствующая подпрограмма: $text =~ s/(&.*?;)/&esc2char($1)/egs;
т.е. из регулярного выражения происходит вызов подпрограммы.
g - заменить все одинаковые компоненты, а не один, как в отсутствии ключа g.
i - не учитывать регистр.
m - строка, в которой происходит поиск, состоит из множества строк.
s - строка, в которой происходит поиск, состоит из одной строки.
x - сложный шаблон, т.е. можно писать не в строчку, а для упрощения понимания разбивать шаблон на несколько строк, примеры об этом ниже.
o - компилировать шаблон один раз.
Допустим нужно сделать поисковик, который ходит по директориям на сервере, но некоторые директории типа /cgi-bin/ и т.п. индексировать нельзя. Объявляем переменную, которая будет содержать регулярное выражение, в данном случае перечисление или img или image или temp или tmp или cgi-bin:
$no_dir = '(img|image|temp|tmp|cgi-bin)';
Ключи регулярного выражения m#$no_dir$#io говорят о том, что компилировать содержимое $no_dir нужно только один раз(ключ o) и также еще не учитывать регистр(ключ i).
Оператор tr/выражение1/выражение2/, ключи cds
Смысл: замена выражения1 на выражение2. Если указан ключ с, то это инверсия выражения1, т.е. в выражение один не входят содержащиеся в нем символы. если указа ключ d, то значит стереть замененные символы. Если указан ключ s, то значит заменить многочисленные повторяющиеся символы на одиночный символ.
Оператор y/выражение1/выражение2/(ключи cds), равносилен оператору tr.
Например в поисковой системе нужно приводить запрос в нижний регистр, дабы не зависеть от настроек локали:
$CAP_LETTERS = '\xC0-\xDF\xA8'; $LOW_LETTERS = '\xE0-\xFF\xB8';
$code = '$html_text =~ '; $code .= "tr/A-Z$CAP_LETTERS/a-z$LOW_LETTERS/"; $down_case = eval "sub{$code}";
Other
Вывод строк из заданного интервала для данной строки:
if(/pattern1/i .. /pattern2/i){...} #истинность первого оператора включает конструкцию, а второго е выключает. if($nomer1 .. $nomer2){...}
... не возвратит истину, в отличии от .., если условия выполняются в одной строке.
if(/pattern1/i ... /pattern2/i){...} if($nomer1 ... $nomer2){...}
для многострочного файла
print -ne 'print if 3 .. 15' file.txt
выведет строки файла с 3 по 15 строчку, та-же самая опреация но немного по другому:
open F, "<file"; while(<F>){ print if(3 .. 15) }
или с какой нибудь начальной и конечно разметкой, например есть вспомогательный файл шаблонов(просто различные виды html, в зависимости от действия пользователя) для разных определенны случаев, которые нужны исходя из контекста программы:
open F, "<file"; while(<F>){ print if(/<!--begin welcome-->/i ... /<!--end welcome-->/i) }
Такая конструкция позволяет выводить куски многострочного html кода(для однострочного нужно ставить оператор ..).
Условия в таких операторах можно ставить и разнотипными $file=qr/2345/; while(<F>){ print if(/^$/ .. 10); #увидим, что находится от пустой до 10-й строки print if(/^\001/ .. /$file/); #выведет все, что после нуля и до того что задано qr }
Программа чтения почтовых адресов из mbox или sent-mail: while(<F>){ next unless /^From:?\s/i .. /^$/; while (/([^<>(,;)\s]+\@[^<>(,;)\s]+)/)g{ print "$1\n" unless $test{$1}++; } }
запускается ./regex.pl /root/mail/sent-mail и выводит каждый емейл по одному разу.
П2.1.1 Введение
Perl - интерпретируемый язык, приспособленный для обработки произвольных текстовых файлов, извлечения из них необходимой информациии и выдачи сообщений. Perl также удобен для написания различных системных программ. Этот язык прост в использовании, эффективен, но про него трудно сказать, что он элегантен и компактен. Perl сочитает в себе лучшие черты C, shell, sed и awk, поэтому для тех, кто знаком с ними, изучение Perl-а не представляет особого труда. Cинтаксис выражений Perl-а близок к синтаксису C. В отличие от большинства утилит ОС UNIX Perl не ставит ограничений на объем обрабатываемых данных и если хватает ресурсов, то весь файл обрабатывается как одна строка. Рекурсия может быть произвольной глубины. Хотя Perl приспособлен для сканирования текстовых файлов, он может обрабатывать так же двоичные данные и создавать .dbm файлы, подобные ассоциативным массивам. Perl позволяет использовать регулярные выражения, создавать объекты, вставлять в программу на С или C++ куски кода на Perl-е, а также позволяет осуществлять доступ к базам данных, в том числе Oracle.
Ниже приводится в качестве примера небольшая программа, которая осуществляет доступ к Oracle. #! /usr/local/bin/perl -w
# запуск с ключом печати ошибок.
use Oraperl;
# подключение модуля для работы с Oracle
$system_id = 'T:bdhost.somwere.com:Base';
# описание имени базы данных
$lda = &ora_login($system_id, 'scott','tiger');
# подключение к базе данных пользователя
# scott с паролем tiger
$statement = 'create table MYTABLE
(NAME char(10), TITLE char(20),
ORGANIZATION varchar2(256))';
# SQL команда создания таблицы MYTABLE
$csr = &ora_open($lda, $statement)
die $ora_errstr;
# открытие курсора csr, если это невозможно,
# то осуществляется выход и печать сообщения об ошибке.
&ora_close($csr); # закрытие курсора
open(FL, '/home/my_home_directory/my_file')
die "Can't open file \n";
# открытие файла, если это невозможно,
# то выход с печатью сообщения об ошибке
while ($string = <FL>) {
# чтение входного потока из файла с дескриптором FL
@array = split(/\//,$string);
# считанная строка разбивается в массив
# строк, разделителем служит слеш
$st = 'insert into MYTABLE values(:1,:2,:3)';
# SQL команда помещения в таблицу некоторых значений
$csr = &ora_open($lda,$st) $ora_errstr;
# открытие курсора или печать
# сообщения об ошибке и выход
&ora_bind($csr,$array[0],$array[1],$array[2]);
# привязка значений переменных к величинам, которые
# требуется поместить в таблицу
&ora_close($csr); # закрытие курсора
}
close FL; # закрытие файла
&ora_commit($lda);
# завершение транзакции в базе данных
&ora_logoff($lda);
# отключение от базы данных
П2.2.1 Скалярные величины
Скалярные переменные могут содержать различные простые типы данных, такие как числа, строки или ссылки. Они не могут содержать сложные типы, но могут содержать ссылку на массив или хэш. В булевском контексте скаляр принимает значение TRUE, если он содержит не нулевое число или не пустую строку. В Perl существует несколько способов записи чисел: ¯
12345
12345.67
.23E-10
0xffff шестнадцатеричная запись
0377 восьмеричная запись.
1_234_567_890 подчерк для удобства чтения.
Строки заключаются в одинарные или двойные кавычки. Использование кавычек в Perl такое же как в bourne shell-е: строка в двойных кавычках обрабатывается и вместо переменных подставляются их значения, а также обрабатываются бакслэш-последовательности, строки в одинарных кавычках рассматривается просто как последовательности символов. Используются также: ¯
\t табуляция
\n перевод строки
\r возврат каретки
\b пробел
\e символ Escape
\033 восьмеричный формат
\x1b шестнадцатеричный формат
\c[ управляющая последовательность (control)
\a сигнал (alarm)
\f переход на следующую страницу
П2.2.2 Простые массивы
Perl позволяет произвольно удлинять массив путем обращения к элементу, индекс которого больше, чем последний индекс массива. Так же можно произвольно уменьшить длину массива.
@day = ('a','b');
$day[3] = 'c';
Теперь массив day содержит три элемента: ('a','b','c').
@day = (); или, что то же самое: $#day = -1;
Теперь массив day пуст.
(@day, @month, &SomeSub) содержит в себе элементы массива day, за ними следуют элементы массива month, а за ними результат выполнения подпрограммы SomeSub. Пустой массив обозначается (). Массив ((),(),(),()) эквивалентен пустому массиву. Последний элемент массива может быть массивом или хэшэм:
($a, $b, @c)= split;
($a, $b, %c)= @_;
Любой элемент массива может быть массивом.