Практический Перл для начинающего
Источник: http://www.masteru.ru/
Данная статья написана для людей, которым в силу
непреодолимых обстоятельств приспичило срочно изучить Перл. Для меня
таким обстоятельством стало то, что мой компьютер стал WEB-сервером,
а я, соответственно, WEB-мастером. Учиться принято на чужих ошибках
и опыте, поэтому предлагаю Вашему вниманию свой опыт изучения
Перла.
Сразу нужно пояснить, для кого это все написано.
Если Ваш сервер работает на платформе UNIX, то это я должен читать
Вашу статью. У меня же установлен Windows NT workstation 4.0 (RUS)
плюс Service Pack 3. Когда пришло время сделать из компьютера
WEB-сервер, я было кинулся ко встроенным Службам узла WEB, но быстро
понял, что это мне не нравится (почему ?). И тут один добрый человек
посоветовал поставить Xitami WEB Server от iMatix Corporation (http://www.imatix.com/), который и стоит по сей
день.
Что касается самого Перла, то здесь несколько
сложнее. Покопавшись по различным Перловым серверам (www.perl.org ,
www.perl.com ) я узнал, что версий Перла настолько много, что
выбрать что-нибудь конкретное довольно сложно. При этом каких-нибудь
вразумительных рекомендаций по поводу выбора той или иной версии
нигде нет. Перепробовав почти все версии для Windows, я остановил
свой выбор на Active Perl (http://www.activestate.com/).
Человеку, избалованному всякими Виндовозами и
Дельфями, писать программы на Перл довольно непривычно, поэтому
настоятельно рекомендую сразу установить Perl Builder. Взять его
можно на www.solutionsoft.com. Там лежала тридцатидневная Демо
версия.
Ну, думаю, пора переходить непосредственно к делу.
В общем случае, скрипт на Перл, как и любая другая программа,
работает так:
- получает данные
- обрабатывает данные
- выдает результаты
Передать данные скрипту можно двумя методами - GET
и POST. Разница между ними в том, что при использовании GET данные
постоянно болтаются в строке адреса браузера, напимер:
httр://treagraf.tasur.edu.ru/cgi-bin/price.pl?Category=POWER&Description=varta
В этом случае скрипт B_price.pl берет
данные в переменной окружения QUERY-STRING.
$data=$ENV{'QUERY_STRING'};
При использовании метода POST данные передаются на
стандартный вход скрипта. Длинна блока данных берется в переменной
CONTENT_LENGTH:
read(STDIN,$data,$ENV{'CONTENT_LENGTH'});
Теперь эти данные нужно перевести в удобоваримый
вид, поскольку они закодированы.
Стандартным соглашением служит замена пробелов
знаками плюс и затем кодировка оставшихся недопустимых символов с
помощью ASCII-кодов в шестнадцатиричной форме, перед которыми
ставится знак (%). Пример:
http://treagraf.tasur.edu.ru/cgi-bin/B_price.pl?Category=
%C2%E8%E4%E5%EE&Description=%E0%E1%E2%E3
Это значит:
http://treagraf.tasur.edu.ru/cgi-bin/B_price.pl?Category=Видео&Description=абвг
Декодировать строку запросов в первый раз лучше
самому. На вопрос "а как?" есть множество ответов, переписывать
которые нет смысла. Приведу лишь короткий пример:
Заменяем знаки (+) на пробелы
$query = ~ s/\+/ /g;
Потом заменяем все сочетания знака (%), после
которого следуют шестнадцатиричные цифры, на соответствующий символ
ASCII
$query =~ s/%([0-9A-H]{2})/pack('C', hex($1))/eg;
Я пользуюсь тем, что предлагает Perl Builder:
#!
E:\perl5\bin\perl
&GetFormInput;
# вызов подпрограммы получения данных
$Category =
$field{'Category'}; #
получаем данные из поля Category
$Description =
$field{'Description'}; # получаем данные из поля
Description
$Page =
$field{'Page'};
# получаем данные из поля Page
В конце скрипта помещаем подпрограмму "прозрачного"
чтения данных.
sub GetFormInput {
(*fval) = @_ if @_ ;
local
($buf);
if ($ENV{'REQUEST_METHOD'} eq 'POST')
{
read(STDIN,$buf,$ENV{'CONTENT_LENGTH'});
}
else
{
$buf=$ENV{'QUERY_STRING'};
}
if ($buf eq "") {
return
0 ;
}
else {
@fval=split(/&/,$buf);
foreach
$i (0 .. $#fval){
($name,$val)=split
(/=/,$fval[$i],2);
$val=~tr/+/ /;
$val=~
s/%(..)/pack("c",hex($1))/ge;
$name=~tr/+/ /;
$name=~
s/%(..)/pack("c",hex($1))/ge;
if (!defined($field{$name}))
{
$field{$name}=$val;
}
else {
$field{$name} .=
",$val";
#if you want multi-selects to goto into #an array
change to:
#$field{$name} .=
"\0$val";
}
}
}
return 1;
}
Второй этап работы скрипта - обработка данных -
полностью на Ваше усмотрение. Проверяйте полученные данные на
правильность, пишите их в файл, делайте что хотите.
И, наконец, Вам нужно выдать какие-то результаты
броузеру клиента, причем так, чтобы броузер правильно их отобразил.
То есть, выдавать результаты нужно в HTML. Это делается просто:
(тоже можно по-разному)
print 'Content-type: text/html',
"/n/n";
#обязательная строка
print '
В поле Category Вы ввели: ', $Category, '
',"\n"
Все это касается скриптов, получающих данные из
формы на странице HTML. При этом страница с формой - отдельно,
скрипт - отдельно. Можно сделать красивее и удобнее: объединить
страницу и скрипт в единое целое. Для этого скрипт пишется по
схеме:
- При первом запуске скрипт рисует HTML страницу с формой и
ссылкой в тэге ACTION на самого себя. Первый запуск определяется
по отсутствию входных данных.
- Если входные данные есть, то получаем их, обрабатываем и
выдаем результаты.
Пример:
#! E:\perl5\bin\perl
if (($ENV{'QUERY_STRING'} eq '') or
($ENV{CONTENT_LENGTH}=0) )
{ # генерируем страницу с
формой }
else
{# получаем данные, обрабатываем и выдаем результат}
Гостевая книга
Общий алгоритм работы гостевой книги таков:
1. Если посетитель хочет сделать запись в книгу,
то
1.1 Получаем данные
1.2 Записываем их в файл или в базу
данных
1.3 Говорим спасибо на HTML и предлагаем почитать другие
записи
2. Если посетитель хочет почитать записи в книге,
то
2.1 Читаем записи из файла или из базы данных
2.2 Выводим
их красиво в HTML
Для удобства восприятия я оформил пункты 1 и 2
отдельными скриптами add_guestbook.pl и
read_guestbook.pl соответственно. Сообщения гостевой книги
хранятся в текстовом файле построчно, т.е. на каждую запись -
строка. Так сделано для удобства чтения этого файла. Пример одной
записи:
Sat Dec 5 13:31:20 1998&
Наташа&студентка&Good&Для начала хорошо. Успехов на
данном поприще Вам, Александр!&нету@пока&194.226.60.34
Вот описание полей рассматриваемой гостевой
книги.
Name - имя, фамилия, отчество, кличка - на усмотрение
посетителя
Work - профессия, род занятий
RadioButton - три
кнопки: понравилось (Good), не понравилось (Bad), пофигу
(Different)
Text - text box комментариев и примечаний
Email -
обратный адрес
add_guestbook.pl - запись в книгу
#! e:\perl5\perl
# Первая строка, как обычно
require
"ssi-pl.pl";
# Я использую навигационную панель в виде
SSI-включения. Для этого используется модуль ssi-pl.pl
if
(($ENV{'QUERY_STRING'} eq '') or ($ENV{CONTENT_LENGTH}=0) )
{
# Если нет входных данных, то генерируем страницу с
формой
print <
<head>
<meta http-equiv="Content-Type"
content="text/html; charset=windows-1251">
<meta
name="GENERATOR" content="Microsoft FrontPage
3.0">
<title>Книга жалоб и
предложений</title>
</head>
<body
background=" ../images/background_new.jpg">
<div
align="left">
<table border="0" width="630"
height="49">
<tr>
<td
width="200" height="45"></td>
<td
width="430" height="45">< p align="center"><img
src="../images/guestbook.GIF"
alt="Книга
жалоб" WIDTH="258" HEIGHT="60"></td>
</tr>
</table>
</div><div
align="left">
<table border="0" width="630" height="53"
cellspacing="0" cellpadding="0">
<tr>
<td width="200" height="260"
valign="top">
<p
align="center">
HTML
DoInclude("_menu.htm"); # Это SSI-включение
навигационной панели.
print
<<HTML;
</p>
<p align="
left"> </td>
<td width=" 10"
height="53" valign="top"></td>
<td
width=" 410" height="53" valign="top"><table border="1"
width="100%" cellspacing="0"
cellpadding="0">
<tr>
<td
width="100%">< form name="GuestBook" method="POST" action="
add_guestbook.pl">
<div align="left"><p> <small>Я,
<input type="text" name="Name" size="20"></small> ,
<small>по
профессии простой </small><input
type="text" name="Work" size="20"> ,
<small>посетив
данный сервер и ознакомившись с
представленными
на нем материалами, хочу выразить свои чувства
и
эмоции
следующими приличными
словами:</small></p>
</div><div
align="left"><p><small> </small><input
type="radio" value="Good"
checked
name="RadioButton"><small>мне понравилось
:-)</small></p>
</div><div
align="left"><p><small> </small><input
type="radio"
name="RadioButton"
value="Bad"><small>мне не понравилось :-(
</small></p>
</div><div align="left"><p> <input
type="radio" name="RadioButton"
value="Different"><small>мне
пофигу :-|
</small></p>
</div><div align="left"><p><small>В
дополнение к сказанному
хочу
так
же
сказать: </small></p>
</div><div align="left"><p><textarea rows="4"
name="Text"
cols="30"></textarea></p>
</div><div align="left"><p><small>Прошу
принять к
рассмотрению
мое заявление и незамедлительно принять
меры.
Решение по моему заявлению направить
письменно
на мой электронный адрес </small><input type="text"
name="Email"
size="20"><small>.</small></p>
</div><div
align="center"><center><p><input
src="../images/send.JPG"
name="Send"
alt="Послать" border="0" type="image" WIDTH="53"
HEIGHT="21">
<a
href="read_guestbook.pl"><img src="../images/read.jpg"
alt="Почитать"
border="0"
WIDTH="63"
HEIGHT="21"></a></p>
</center></div>
</form>
</td>
</tr>
</table>
</td>
<td width="10" height="53"
valign="top"></td>
</tr>
</table>
</div>
</body>
</html>
HTML
die;
}
#
Теперь получаем входные
данные.
&GetFormInput;
$Name = $field{'Name'}
;
$Work = $field{'Work'} ;
$RadioButton =
$field{'RadioButton'} ;
$Text = $field{'Text'} ;
$Email =
$field{'Email'} ;
$Send = $field{'Send'} ; # это поле не
используется
# Проверяем, заполнены ли обязательные
поля.
# Если нет - генерируем HTML страницу с просьбой заполнить
нужные поля.
if ($Name eq '' || $Email eq '' || $Text eq
'')
{
print <<HTML;
Content-type:
text/html
<html>
<head>
<meta
http-equiv="Content-Type" content="text/html;
charset=windows-1251">
<meta name="GENERATOR"
content="Microsoft FrontPage 3.0">
<title>Книга жалоб и
предложений - ошибка</title>
</head>
<body
background=" ../images/background_new.jpg">
<div
align="left">
<table border="0" width="630"
height="49">
<tr>
<td
width="200" height="45"></td>
<td
width="430" height="45"><p align="center"><img
src="../images/guestbook.GIF"
alt="Книга
жалоб" WIDTH="258" HEIGHT="60"></td>
</tr>
</table>
</div><div
align="left">
<table border="0" width="630" height="53"
cellspacing="0" cellpadding="0">
<tr>
<td width="200" height="260"
valign="top"><p align="center">
HTML
DoInclude("D:/InetPub/wwwroot/_menu.htm");
print
<<HTML;
</p>
<p
align="left"> </td>
<td
width="10" height="53"
valign="top"></td>
<td width="410"
height="53" valign="top"><p align="left"><small>Вы не
указали
свое имя, E-mail, либо не заполнили
сам текст Вашего
отзыва. Вернитесь,
пожалуйста, на страницу формы
и заполните
требуемые поля.</small></p>
<p
align="center"><a href="add_guestbook.pl">Назад</a>
</td>
</tr>
</table>
</div>
<table>
<tr>
<td width="10" height="53"
valign="top"></td>
</tr>
</table>
</body>
</html>
HTML
}
else # все данные правильно введены
{
# Если все
поля заполнены правильно, то начинаем их обрабатывать.
$Text=~tr/\r\n/ /; #заменяем перевод строки на
пробел
# Если в текстовом поле формы (text box) посетитель
нажимал Enter,
# то нужно убрать символы перевода строки, чтобы
можно было записать
# все поля формы в одну строку
файла.
if ($Work eq '') {$Work=' '}; #если пусто - то
пробел
# Если поле не заполнено, то оно равно
пробелу.
$Name=~s/&/ /g;
$Work=~s/&/
/g;
$Text=~s/&/ /g;
$Email=~s/&/ /g;
# Если
посетитель использовал символ &, то заменяем его на пробел,
#
поскольку этот символ мы будем использовать для разделения наших
полей в файле.
open(OutFile, ">>guestbook.txt") ||
die;
# Открываем файл для
добавления.
$Time=localtime;
#получаем время
# Получаем время заполнения гостевой
книги.
$line=join('&', $Time, $Name, $Work, $RadioButton,
$Text, $Email, $ENV{REMOTE_HOST});
# И, наконец, слепляем все
поля формы в одну строку. На всякий случай добавляем в конце
# IP
адрес посетителя, взятый из переменных окружения.
print
OutFile "$line\n";
close OutFile;
# Записываем полученную
строку в файл и закрываем его.
# Осталось только сказать
посетителю спасибо.
# выводим сообщение о успехе
print
"Content-type: text/html\n\n";
print "<html>\n" ;
print
"\n" ;
print "<head>\n" ;
print '<meta
http-equiv="Content-Type" content="text/html;
charset=windows-1251">'."\n" ;
print '<meta
name="GENERATOR" content="Microsoft FrontPage 3.0">'."\n"
;
print "<title>Книга жалоб и предложений</title>\n"
;
print "</head>\n" ;
print "\n" ;
print '<body
background="../images/background_new.jpg"> '."\n" ;
print
'<div align="left">'."\n" ;
print "\n" ;
print
'<table border= "0" width="630" height="49"> '."\n" ;
print
" <tr>\n" ;
print ' <td
width="200" height="45"> </td>'."\n" ;
print
' <td width="430" height="45">< p align="
center">';
print '<img src=" ../images/guestbook.GIF"
alt="Книга жалоб" WIDTH="258" HEIGHT="60"></td>'."\n"
;
print " </tr>\n" ;
print "</table>\n"
;
print '</div><div align="left">'."\n" ;
print
"\n" ;
print '<table border="0" width="630" height="53"
cellspacing="0" cellpadding="0"> '."\n" ;
print "
<tr>\n" ;
print ' <td width="200"
height="260" valign="top"><p align="center"> '."\n"
;
DoInclude("D:/InetPub/wwwroot/_menu.htm");
print
' <p align="left"> </td>'."\n"
;
print ' <td width="10" height="53"
valign="top" ></td>'."\n" ;
print '
<td width="410" height="53" valign="top"><p align="center"
><small>Ваши данные'."\n" ;
print "
приняты. Спасибо.</small></p>\n" ;
print
' <p align="center"><a
href="read_guestbook.pl">';
print '<img
src="../images/read.jpg" alt="Почитать" border="0" WIDTH="63"
HEIGHT="21"> </a> </td>'."\n" ;
print "
</tr>\n" ;
print "</table>\n" ;
print
"</div>\n" ;
print "\n" ;
print "<table>\n"
;
print " <tr>\n" ;
print '
<td width="10" height="53" valign="top"></td>'."\n"
;
print " </tr>\n" ;
print "</table>\n"
;
print "</body>\n" ;
print "</html>\n"
;
}
# Не забываем подпрограмму разбора данных из
формы.
sub GetFormInput {
(*fval) = @_ if @_
;
local ($buf);
if ($ENV{'REQUEST_METHOD'} eq 'POST')
{
read(STDIN,$buf,$ENV{'CONTENT_LENGTH'});
}
else
{
$buf=$ENV{'QUERY_STRING'};
}
if ($buf eq "") {
return
0 ;
}
else {
@fval=split(/&/,$buf);
foreach
$i (0 .. $#fval){
($name,$val)=split
(/=/,$fval[$i],2);
$val=~tr/+/ /;
$val=~
s/%(..)/pack("c",hex($1))/ge;
$name=~tr/+/ /;
$name=~
s/%(..)/pack("c",hex($1))/ge;
if (!defined($field{$name}))
{
$field{$name}=$val;
}
else {
$field{$name} .=
",$val";
#if you want multi-selects to goto into an array
change to:
#$field{$name} .=
"\0$val";
}
}
}
return
1;
}
Вот и все. Пример работы описанного скрипта можно
посмотреть на http://treagraf.tasur.edu.ru/cgi-bin/add_guestbook.pl
read_guestbook.pl - чтение книги
#! e:\perl5\perl
# Первая строка, как обычно
require
"ssi-pl.pl";
# Я использую навигационную панель в виде
SSI-включения. Для этого используется модуль
ssi-pl.pl
open(InFile, "guestbook.txt") || die;
#
Открываем файл с записями гостевой
книги.
@lines=<InFile>;
# Читаем строки в
массив.
# Выдаем шапку HTML страницы.
print
<<HTML;
Content-type:
text/html
<html>
<head>
<meta
http-equiv="Content-Type" content="text/html;
charset=windows-1251">
<meta name="GENERATOR"
content="Microsoft FrontPage 3.0">
<title>Книга жалоб и
предложений - нам
пишут</title>
</head>
<body background="
../images/background_new.jpg">
<div
align="left">
<table border="0" width="630"
height="49">
<tr>
<td
width="200" height="45"></td>
<td
width="430" height="45"><p align="center"><img
src="../images/guestbook.GIF"
alt="Книга
жалоб" WIDTH="258" HEIGHT="60"></td>
</tr>
</table>
</div><div
align="left">
<table border="0" width="630" height="53"
cellspacing="0" cellpadding="0">
<tr>
<td width="200" height="260"
valign="top" ><p
align="center"><small>
HTML
DoInclude(" D:/InetPub/wwwroot/_menu.htm");
print
<<HTML;
</p>
<p align="left"> </td>
<td
width="10" height="53" valign="top"
></td>
<td width="410" height="53"
valign="top" ><p align="center">Нам
пишут:</p>
<table border="0"
width="100%" cellspacing=" 0" cellpadding="0">
HTML
#
Теперь выводим записи в невидимой (в смысле, рамка не видима)
таблице.
# Чтобы свежие записи отображать первыми, обрабатываем
массив строк с конца.
for ($i=$#lines; $i>=$[; $i--)
#обрабатываем строки файла с конца
{
# Разделяем строку на
части
@item=split('&', $lines[$i]); #разделяем на
части
# Теперь заменяем HTML тэги в записи (на случай
какого-нибудь хитрого юзера)
foreach
(@item)
{
$_=~s/</</g;
$_=~s/>/>/g;
}
#
Приступаем непосредственно к выводу записей в HTML
print
"<tr>\n";
print '<td width="100%"><dl>
'."\n";
# В зависимости от поля, где посетителю предлагался
выбор понравилось - не понравилось,
# рисуем картинку с веселой
или грустной мордочкой соответственно. В качестве ALT тэга
#
картинки пропишем IP адрес посетителя.
print '<dt>< img
src="../images/'.$item[3].'.gif" width="31" height="31"
alt="';
priny $item[6].'" align="absbottom"'."\n";
#
Выводим остальные поля.
print 'align="absmiddle"
><small>'.' '.$item[4]."</small>
</dt>\n";
print '<dt><small>
'.$item[1].', '.$item[2]."</small>
</dt>\n";
print '<dt><a
href="mailto:'.$item[5].'"> <small>'.$item[5].'<
/small></a></dt>'."\n";
print
'<dt><small>'.$item[0]."</small></dt>\n";
print
"</dl>\n";
print "</td>\n";
print
"</tr>\n";
}
# Осталось вывести окончание HTML
print <<HTML;
</table>
</td>
<td width="10" height="53"
valign="top"></td>
</tr>
</table>
</div>
</body>
</html>
HTML
close
InFile;
# Закрываем файл с записями гостевой книги.
Вот и все. Пример работы смотрите на http://treagraf.tasur.edu.ru/cgi-bin/read_guestbook.pl
--
Александр Боровский

Реклама в журнале