
<!doctype linuxdoc system>

<article>

<title>FAQ по perl и программированию для web
<author>Составил Павел Аммосов, <tt/apavel@aha.ru/
<date>08 июня 2001
<abstract>
Русскоязычный сборник часто задаваемых вопросов по языку программирования perl 
и server-side web programming.
</abstract>

<toc>

<sect>Где взять перл и все с ним связанное<label id="Где взять перл и все с ним связанное"><p>

<sect1>Где можно взять перл?<p>

 На CPAN == Comprehensive Perl Archive Network.  Homepage CPAN --
http://www.cpan.org/ -- там берут все, связанное с перлом.  Собственно
исходники новейшей версии перла берут из файла
http://www.perl.com/CPAN/src/latest.tar.gz (на самом деле с www.perl.com 
стоит редирект на ближайший, по мнению www.perl.com, mirror).

<sect1>Где можно скачать перловскую библиотеку для работы с  WWW?<p>

 Там же, где и все, относящееся к перлу -- на CPAN.  Полный
список всех модулей и библиотек -- <url url="http://search.cpan.org/" name="http://search.cpan.org/">


<sect1>C www.cpan.org слишком долго качается, нет ли где поближе?<p>

 Есть. Полный список зеркал находится в
<url url="ftp://ftp.nluug.nl/pub/languages/perl/CPAN/MIRRORED.BY" name="ftp://ftp.nluug.nl/pub/languages/perl/CPAN/MIRRORED.BY">, там можно
найти funet или что там поближе, или даже российские зеркала, например
<url url="ftp://ftp.aha.ru/pub/CPAN" name="ftp://ftp.aha.ru/pub/CPAN">


<sect1>Я скачал модуль Lib::Module. Как его установить?<p>
 Обычно модуль приходит в формате tar+gzip, типа module-0.01.tar.gz.
Вам необходимо его развернуть: tar zxf module-0.01.tar.gz
и перейти в образовавшуюся директорию, например module-0.01: cd module-0.01
Обычно там находятся несколько файлов. Для вас будут важными следующие:<p>
<descrip>
<tag>README</tag> что это за модуль
<tag>INSTALL</tag> как его поставить
<tag>Makefile.PL</tag> перловый скрипт для генерации Makefile
</descrip>

Обычно инсталляция происходит следующим образом:
<descrip>
<tag><tt>perl Makefile.PL</tt></tag> генерация Makefile
<tag><tt>make all</tt></tag>сборка модуля
<tag><tt>make test</tt></tag>тестирование модуля
<tag><tt>make install</tt></tag>инсталляция модуля
</descrip>

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

Но, если вы их не имеете, то можете инсталлировать модуль у себя.
Для этого вам необходимо выбрать, где это будет делаться. Для примера,
в $HOME/lib/perl5. Необходимо создать эти директории, если их нет:
<verb> 
 mkdir ~/lib
 mkdir ~/lib/perl5</verb>
Затем, при генерации Makefile для модуля, вы должны указать, где вы
хотите инсталлировать модуль:perl Makefile prefix=$HOME
Все остальные шаги без изменений. В результате вы получите
альтернативное место, где у вас будут находиться модули.
Например, у меня это выглядит так:
<tscreen><verb>
  artur@disco:~ (681) ls -alFg ~/lib/perl5/
  total 704
  drwxr-sr-x   8 artur    staff        512 Oct 06 15:53 ./
  drwxr-sr-x   9 artur    staff       1024 Oct 08 10:50 ../
  drwxr-sr-x   4 artur    staff        512 Mar 31 1998  aix/
  drwxr-sr-x   2 artur    staff        512 Aug 06 13:40 CPAN/
  -r--r--r--   1 artur    staff     126951 Jul 24 19:37 CPAN.pm
  drwxr-sr-x   3 artur    staff        512 Mar 31 1998  man/
  drwxr-sr-x  31 artur    staff       1024 Sep 09 15:41 site_perl/
</verb></tscreen>

Для того, чтобы их использовать, есть несколько путей:

<enum>
<item> указывать при запуске perl в командной строке:
  <verb>perl -I$HOME/lib/perl5 -I$HOME/lib/perl5/site_perl script.pl</verb>
<item> завести переменную шелла PERL5LIB:
  для sh (bash,zsh,ksh)
  <verb>PERL5LIB=$HOME/lib/perl5:$HOME/lib/perl5/site_perl</verb>
  export PERL5LIB
  для csh (tcsh)
  <verb>setenv PERL5LIB $HOME/lib/perl5:$HOME/lib/perl5/site_perl</verb>
<item> указывать в начале скриптов дополнительные библиотеки:
<verb>
use lib "/home/you/lib/perl5";
use lib "/home/you/lib/perl5/site_perl";</verb>
Данный случай подходит к CGI-скриптам, которые не наследуют ваших
переменных окружения.
</enum>

<sect1>Как автоматизировать процесс установки библиотек и модулей?<p>

 Скачивание и установка вручную -- достаточно сложное занятие,
требующее постоянного вмешательства с вашей стороны. 
Можно простым способом: сделать su (желательно), запустить 
<tt>perl -MCPAN -e shell</tt>. Если это первый запуск модуля CPAN, 
вам зададут серию вопросов на тему URL вашего любимого зеркала CPAN. После 
этого все просто: если вы знаете название модуля или библиотеки, то
набираете, скажем, <tt>install Net::NNTP</tt>.  Тогда этот модуль (для работы с
NNTP) сам ищется на CPAN, сам скачивается, компилируется,
устанавливается.  Все, поставили, можно выходить и использовать вновь
установленный модуль.  Если название точно не известно, то набираем 
там же help и используем различные команды поиска типа i NNTP.

<sect1>У меня нет root-привилегий. Можно ли использовать модуль CPAN для установки модулей в этом случае?<p>
 При использовании CPAN-модуля вам необходимо сконфигурировать его
таким образом, чтобы он сам инсталлировал модули туда, куда вы
укажете. Для этого вам необходимо подредактировать файл
<tt>~/.cpan/CPAN/MyConfig.pm:</tt> указать в строке
<tt>&amp;makepl_arg&amp; =&amp; q[prefix=/home/you],</tt>
и теперь всегда при автоматической инсталляции модули будут ставится к
вам в $HOME/lib/perl5
Более подробно обо всем этом можно прочитать в документации к
<tt>ExtUtils::MakeMaker.</tt>

NB: Современный Модуль СPAN все сам спросит и сделает.

<sect>Где взять документацию по перлу<label id="Где взять документацию по перлу">

<sect1>Какие есть книжки про Перл?<p>
По perl, есть много хороших книжек, о которых я вкратце расскажу и еще 
больше плохих, о которых упоминать не стоит.  В целом, за некоторыми
исключениями правило просто -- если оригинал выпустило издательство 
O'Reilly, то эту книгу стоит читать :-)

Рандал Шварц, Том Кристиансен.  Изучаем перл (aka Llama book)
(В оригинале Learning Perl By Randal Schwartz, Tom Christiansen &amp; 
Larry Wall)

Книга для начинающих, рассказано что такое perl, разобраны все основные
конструкции языка, регулярные выражения, использование CPAN и написание 
CGI-программ.

Llama book я рекомендую всем для знакомства с perl.

Том Кристиансен, Натан Торкингтон, Perl: библиотека программиста 

(В оригинале Perl Cookbook By Tom Christiansen &amp; Nathan Torkington)

В этом толстом 700-страничном томе разобрана куча практических
вопросов, возникающих при написании программ на perl: от
преобразования регистров символов до написания TCP-серверов.
  
Скотт Гулич, Шишир Гундаварам, Гюнтер Бирзнекс: CGI программирование на 
Perl

(В оригинале CGI Programming with Perl, second edition by 
Scott Guelich, Shishir Gundavaram and Gunter Birznieks)

Подробное руководство по написанию CGI-программ.  Начиная с описания 
протокола HTTP и включая такие темы, как отправка почты, работа с 
базами данных, создание графических файлов из программ, XML, модули 
шаблонов и mod_perl.

В perldoc perlbook описаны другие интересные англоязычные книги по perl.

O'Reily в 2000 году выпустил The Perl CD Bookshelf, содержащий
электронные версии большинства своих книг по perl и многие люди 
держат его на своих сайтах.  Я URL давать не буду, они быстро становятся
устаревшими, воспользуйтесь поисковыми системами.

<sect1>Где взять стандартную документацию про такое-то свойство перла?<p>

 Перл приходит с полным набором документации и набором программ
для перевода в разные форматы. Обычно для подробного ознакомления с
некоторой особенностью перла пишут &quot;perldoc perlсвойство&quot; 
или &quot;man perlсвойство&quot;.  Базовый набор "свойств" таков:
<itemize>
<item>     Основы          perldata, perlvar, perlsyn, perlop, perlsub
<item>     Запуск          perlrun, perldebug
<item>     Функции         perlfunc
<item>     Objects         perlref, perlmod, perlobj, perltie
<item>     Data Structures perlref, perllol, perldsc
<item>     Modules         perlmod, perlmodlib, perlsub
<item>     Regexps         perlre, perlfunc, perlop, perllocale
<item>     Moving to perl5 perltrap, perl
<item>     Linking w/C     perlxstut, perlxs, perlcall, perlguts, perlembed
<item>     Various         <url url="http://www.perl.com/CPAN/doc/FMTEYEWTK/index.html" name="http://www.perl.com/CPAN/doc/FMTEYEWTK/index.html">
                     (not a man-page but still useful)

<item>     perl          О перле вообще
<item>     perldelta     Что нового в последней версии перла
<item>     perlfaq       FAQ
<item>     perltoc       Подробное оглавление ко всей документации
<item>     perldata      Типы данных
<item>     perlsyn       Синтаксис языка
<item>     perlop        Арифметические, логические, строковые операции и их приоритет
<item>     perlre        Регулярные выражения (обработка текста и поиск)
<item>     perlrun       Опции командной строки
<item>     perlfunc      Встроенные функции
<item>     perlvar       Специальные переменные
<item>     perlsub       Как писать свои функции (процедуры)
<item>     perlmod       Устройство и принцип работы модулей
<item>     perlmodlib    Модули: создание собственных библиотек
<item>     perlmodinstall Поиск и установка модулей и библиотек на CPAN
<item>     perlform      "Форматы", или шаблоны для выводимых данных
<item>     perllocale    Поддержка интернационализации
<item>     perlref       Ссылки и указатели на данные
<item>     perldsc       Введение в структурные типы данных
<item>     perllol       Структуры данных: массивы и списки 
<item>     perltoot      Введение в объектно-ориентированное программирование
<item>     perlobj       Объекты в перле
<item>     perltie       Связь объектов с обыкновенными переменными
<item>     perlbot       Perl OO tricks and examples
<item>     perlipc       Связь между процессами: pipes, sockets, сигналы и др.
<item>     perldebug     Отладка программ
<item>     perldiag      Сообщения об ошибках
<item>     perlsec       Вопросы безопасности
<item>     perltrap      Возможные грабли и ловушки
<item>     perlport      Как писать портабельные программы
<item>     perlstyle     Стиль программирования на перле
<item>     perlpod       Формат стандартной документации и документация, встраиваемая в исходные тексты программ
<item>     perlbook      О книгах про перл<p>

     --- (для совсем крутых) ---<p>
     
<item>     perlembed     Способы внедрения перл-программ в программы на C/C++
<item>     perlapio      Собственный API, используемый в исходниках перла
<item>     perlxs        XS -- программирование перловских библиотек,
                     используемых вместе с библиотеками на C
<item>     perlxstut     Учебник по XS
<item>     perlguts      Внутренние функции перла для разработчиков
<item>     perlcall      Соглашения о вызове перловских функций из C
<item>     perlhist      История и полный список всех версий перла</itemize>

<sect1>Я под виндами, man у меня нет, где брать стандартную документацию?<p>

 Перл для win32 поставляется с программой perldoc (пользоваться:
"perldoc perlfunc" и т. д.) а также с документацией в HTML: если
хочешь perldoc perlfunc, ищи perlfunc.html.

<sect1>Где взять описание модуля Lib::Module?<p>

 Формат стандартной документации по перлу (pod) обладает
возможностью встраивания прямо в тексты программ, а программа perldoc
-- возможностью извлечения этой документации. Поэтому, чтобы получить
документацию по модулю <tt>Math::Trig</tt>, просто запустите <tt>perldoc Math::Trig</tt>
-- perldoc найдет этот модуль и покажет документацию из него.  Если
модуль не является частью какой-то библиотеки, а существует сам по
себе, например, CGI.pm тогда просто perldoc CGI.pm или даже perldoc
CGI.  NB: программа perldoc не очень расторопна, поэтому при установке
перла под UNIX производится создание manpages для всех модулей, так
что man CGI или man Math::Trig покажет быстрее, чем perldoc.

<sect1>Есть ли подробный большой FAQ по перлу?<p>

 Да, он тоже входит в набор стандартной документации.  Отправная
точка -- perldoc perlfaq, это оглавление, из которого можно узнать,
что FAQ состоит из девяти больших частей.

<sect1>Я крутой разработчик, где лежит описание того, как сделать perldoc/manpage к своей библиотеке?<p>

Про это говорят в perldoc perlpod, в man pod2man.<p>

<sect1>А есть ли где в интернете хорошие доки по перлу?<p>

 Да. На мой взгляд, серия статей Рандала Шварца для Unix Review
Perl Columns -- лучшее введение в перл, и намного интереснее и
полезнее книг Llama и Camel (<em>мнения авторов не всегда совпадают с мнением 
координатора -- Аммосов</em>).  Почитать их можно на
<url url="http://w3.stonehenge.com:80/merlyn/UnixReview/" name="http://w3.stonehenge.com:80/merlyn/UnixReview/">.

Почему я считаю это лучшим введением в перл? Потому что это отдельные
небольшие статьи, каждая из которых иллюстрирует конкретные
возможности перла на примере написания программы для достаточно
простой задачи.  Все же статьи охватывают практически весь спектр
возможностей перла -- от написания скрипта в одну строку, который
может поменять Иванов на Сидоров во всех файлах в дереве директорий,
до основ объектно-ориентированного программирования и принципов
создания собственных модулей и библиотек.

<sect1>А где можно найти русскую доку по перлу?<p>

 Если нет возможности купить в магазине, то есть кое-что и в
Интернете: книга Маслова &quot;Введение в перл&quot;, например.
<url url="http://citforum.ru/koi/internet/perl_tut/" name="http://citforum.ru/koi/internet/perl_tut/"> Есть и другие, особо
хороших не видел.<p>

<sect>Как сделать на перле то-то (основные возможности языка)<label id="Как сделать на перле то-то (основные возможности языка)"><p>

<sect1>Как пользоваться функцией pack (getpwuid, dmbopen)?<p>

 man perlfunc или &quot;perldoc -f имя_функции | pod2text&quot;<p>

<sect1>Как узнать размер файла (дату создания, атрибуты, разрешения на доступ)?<p>

 С помощью так называемых файловых тестов, которые имеют вид
(-тест "имя файла"), например:
<descrip>
<tag>(-s &quot;file.txt&quot;)</tag>размер файла
<tag>-e</tag>существует ли файл
<tag>-w</tag>доступен ли на запись
<tag>-M</tag>количество дней со дня модификации
<tag>-t</tag>является ли терминалом
</descrip>
(Как обычно, это неполный список. См. man perlfunc)

<sect1>Как написать модуль?<p>

Простейший случай:<p>

Сам модуль представляет из себя файл с именем <tt>MyModule.pm</tt>
следующего содержания:
<tscreen><verb>MyModule.pm
package MyModule;
require Exporter;

@ISA = qw(Exporter);

@EXPORT = qw(идентификаторы для экспорта: подпрограммы, имена переменных и
пр.);
# Теперь после того, как вы напишите use MyModule в своей программе, у вас все 
# упомянутые здесь идентификаторы станут видны. Этот метод не рекомендуется,
# так как вы &quot;засоряете&quot; именное пространство программы.

# И/или
@EXPORT_OK = qw(идентификаторы для экспорта: подпрограммы, имена переменных и
пр.);
# Идентификаторы, прописанные здесь будут видны в Вашей программе как
# MyModule::Идентификатор (подпрограммы) или $MyModule::Идентификатор
# (переменные)
# Или, Вы можете написать use MyModule qw(Ид1 Ид2 $Ид3); и все будет как в
# первом случае. Это рекомендуемый вариант.

... ваши подпрограммы и пр ....

1; # Обязательно
</verb></tscreen>


Программа, его использующая: 

<tscreen><verb>myprg.pl
use MyModule qw(...);
# Ну и далее
</verb></tscreen>

Подробности смотрите в perlmod(1), Exporter(3pm), Camel Book

<sect1>Как получить доступ к n-му символу строки?<p>

Надо использовать функцию substr. Например, получить 15й символ из
строки $string:
<verb>$char = substr($string, 15, 1);</verb>
	
Установить с 15го символа строки $string и обрезать ее:
<verb>substr($string, 15) = 'A';</verb>
Можно даже использовать более чем односимвольные последовательности:
<verb>
$ss = substr($string, 15, 5);
substr($string, 15) = 'Хо-Хо';
</verb>

<sect>email<p>

<sect1>Как отправить почту из скрипта на Perl?<p>
<sect2>Простой способ для счастливых владельцев Unix<p>

<tscreen><verb>	
open MAIL, '|/path/to/your/sendmail -t'; # Pipe к sendmail
print MAIL "From: mailrobot@my.host\n",
           "To: webmaster@my.host\n",
	   "Subject: Wow! I got some mail for you!!!\n\n",
	    $message;
close MAIL or die "Sendmail failed: $!";
</verb></tscreen>	

<sect2>Более гибкий способ -- Mail::Mailer<p>
<tscreen><verb>
#!/usr/bin/perl -w
use strict;
use Mail::Mailer;

my $mailer = new Mail::Mailer;
my %headers;
$headers{"To"} = 'apavel';
$headers{"Subject"} = 'Привет';
my $body = "Привет!\nКак дела?";

$mailer->open(\%headers);
print $mailer $body;
$mailer->close;
</verb></tscreen>

<sect1>Как отправить почту с вложениями (attachments)?<p>
<sect2>Используя MIME::Entity из комплекта MIME::Tools<p>
<tscreen><verb>
#!/usr/bin/perl -w
use strict;
use MIME::Entity;

my $message = MIME::Entity->build(
	To      => 'apavel',
	Subject => "Пошлые анекдоты и голые девки",
	Data    => ["Пошлые анекдоты тут"],
	Charset => "koi8-r",
	Encoding=> "8bit",
	);
$message->attach(
	Path     => "naked_girls.jpg",
	Type     => "image/jpg",
	Encoding => "base64",
	Disposition=>"attachment",
	);

open SENDMAIL, "|/usr/sbin/sendmail -t" or die "sendmail: $!";
$message->print(\*SENDMAIL);
close SENDMAIL or die "sendmail failed: $!";
</verb></tscreen>

<sect1>Как определить правильность адреса электронной почты Internet?<p>

Гарантированно -- никак. 
Вы можете проверить адрес на правильность
синтаксиса при помощи Email::Valid.
И все. Если нужен гарантированно правильный e-mail для web-сайта, вы можете 
воспользоваться методом от Алексея Тутубалина: требовать пароль для доступа к
информации, а сам пароль высылать по электронной почте. Тогда человек должен
будет указать свой e-mail, конечно же, если ему интересна эта информация с 
вашего сайта.

<sect1>Как получать почту в Unix?<p>

Самое простое - прописать ваш скрипт в .forward или /etc/aliases:

.forward:
<verb>
| /path/to/your/script
</verb>

/etc/aliases
<verb>
pupkins:  "|/path/to/yours/script"
</verb>

Более подробно смотрите в документации на ваш MTA.

Скрипт должен читать из стандартного потока ввода. Например:
<tscreen><verb>
#!/usr/bin/perl

open (F,">/tmp/mbox") or die $!;
while (<STDIN>) {
   print F $_;
}
close (F);
</verb></tscreen>

<sect1>Как разобрать письмо на составные части?<p>
<sect2>Ручным разбором<p>
письмо состоит из двух частей - заголовка и тела.
Заголовок состоит из пары: имя заголовка и значение заголовка, разделенные 
двоеточием ":",  например:
<tt/test/
(более подробно смотрите в rfc 822, 2822). Тело письма - просто текст.
Пример скрипта для разбора письма.

<tscreen><verb>
#!/usr/bin/perl

# Читаем письмо со стандартного потока ввода STDIN

my (%head, @body);     # %head - пары { header => value }
                      # @body - тело письма
{
	local $/ = "";      # читаем все до пустой строки - разделителя
	my $head = <STDIN>; # заголовка и тела письма
	$head =~ s|\n\s+| |gs; # если значение заголовка длинное,
	                    # то он переносится на следующую строку и
	                    # начинается с пробельного символа. соединяем строку
	my @head = split /\n/,$head;        # делим по строкам заголовок;
	# разделяем строку вида Subject: test на пары "Subject" и "test",
	# и возвращаем их как "subject" => "test"
	%head = map { my ($a,$b) = split (/:\s+/,$_,2); lc $a => $b } @head;
	# теперь в %head собраны все заголовки и их значения
	# предупреждение: я не рассматриваю случай присутствия в заголовке
	# письма одинаковых заголовков (как то "Received:") - как их сделать -
	# ваша задача.
}
@body = <STDIN>;      # теперь в body тело письма, и с ним можно работать.
</verb></tscreen>
Теперь можно смотреть на $head{'content-type'} и решать, что с ним
делать.

<sect2>Используя MIME::Parser<p>
<tscreen><verb>
#!/usr/bin/perl -w
use strict;
use MIME::Parser;

# Построить объект MIME::Parser для разбора письма
my $parser = new MIME::Parser;
# Сохранять временные файлы в /tmp
$parser->output_under("/tmp");
# Разобрать STDIN. 
my $entity = $parser->parse(\*STDIN);

# Теперь $entity -- это объект MIME::Entity

# Получить какой-нибудь заголовок письма можно у $entity->head (объект MIME::Head):
my $subject = $entity->head->get('Subject');

# Если это письмо состоит из одной части, то ее можно получить так:
my $body = $entity->body_as_string;

# ... Но в 1996 netscape выкатила свой MUA, который по-дефолту отправлял одно 
# и тоже сообщение в HTML и text/plain и легкая жизнь для разработчиков
# почтовых систем закончилась. Пришлось разбирать MIME.

if ($entity->is_multipart) {
	for (my $i = 0; $i <  $entity->parts(); $i++) {
		my $part = $entity->parts($i);
		# Теперь $part -- это объект MIME::Entity, содержащий одну из частей этого 
		# этого письма.  Обратите внимание, что $part в некоторых случаях
		# может тоже содержать другие части, например, для MIME типа message/rfc822,
		# так что в общем случае, на этом месте должна быть рекурсивная подпрограмма
		# разбора

		# Открыть эту часть для чтения
		my $IO = $part->bodyhandle->open("r") or die "open body: $!";
		while (defined($_ = $IO->getline)) {
			# Делать чего-то с ней
        }
		$IO->close();
	}	
} else {
	# Это сообщение из одной части:
	$entity->body_as_string;
}	

# Удалить временные файлы
$entity->purge();
</verb></tscreen>

<sect1>Как декодировать строки типа =?koi8-r?B?UmU6IPfFzt?<p>
Можно использовать MIME::Words,
<tscreen><verb>
#!/usr/bin/perl -w
use MIME::Words qw(:all);
use Convert::Cyrillic;
use strict;

my $string = 'Re: =?KOI8-R?B?0sHCz9TB ?= c MIME =?KOI8-R?B?zsE= ?= perl ';

# Простой вариант:
my $decoded = decode_mimewords($subject);
# Но он не выдает информации о кодировке, так что лучше использовать
# другую форму вызова:

my $decoded = join("", map {xcode(${$_}[1], ${$_}[0])} decode_mimewords($string)
print "decoded: $decoded\n";

# процедура перекодирования из чарсета, используемого в сообщении
# в koi8
sub xcode {
	my ($charset, $src) = @_;
	my %charsets = (
		'windows-1251'=>'WIN',
		'iso8859-5'=>'ISO',
		'koi8-r'=>'KOI8',
		'koi8r'=>'KOI8',
		'koi8-u'=>'KOI8',
		'utf-8'=>'UTF8',
		'utf8'=>'UTF8'
	);
	return $src unless ($charsets{lc($charset)});
	Convert::Cyrillic::cstocs($charsets{lc($charset)}, 'KOI8', $src);
}
</verb></tscreen>

<sect1>Как получить почту по POP3?<p>
Используя Net::POP3. 
<tscreen><verb>
#!/usr/bin/perl -w
use strict;
use Net::POP3;

my $server = "oops";

my $pop = new Net::POP3($server);
# Залогинится
my $msgs = $pop->login("alladin", "opensesame"); 
# login возвращает undef, если не удалось залогинится и число сообщений
# в почтовом ящике, если удалось.
die "Login failed" if not defined $msgs;
print "$msgs сообщений\n";
# Список сообщений.  list возвращает ссылку на хэш, ключами которого являются
# номера сообщений, а значениями -- размер письма в байтах.
my %list = %{$pop->list};

for my $msg_num (keys(%list)) {
	print "#$msg_num - $list{$msg_num} байт\n";
	# Получить сообщение.  get вернет ссылку на массив строк
	my $msg = $pop->get($msg_num);
	# сделать с ним чего-нибудь
	open F, ">msg$msg_num" or die "msg$msg_num: $!";
	print F join("", @$msg);
	close F;
	# Отметить сообщение для удаления.  Оно будет удалено 
	# при закрытии коннекта к серверу
	$pop->delete($msg_num);
}

# Закрыть соединение с сервером
$pop->quit();
</verb></tscreen>
<sect>CGI-скрипты<label id="CGI-скрипты"><p>

<sect1>Что такое CGI и как с ним работать<p>
  
 CGI -- Common Gateway Interface.  Стандарт интерфейса внешних программ
с http-сервером. 

<sect2>Как работать<p>
HTTP -- клиент-серверный протокол, следовательно  со стороны CGI-программы, как
серверного процесса, все взаимодействие выглядит следующим образом
<enum>
<item> Получение данных от клиента
<item> Обработка данных
<item> Выдача ответа клиенту.
</enum>

Пункты 1 и 3 я вкратце опишу здесь, а 2, надеюсь, сделаете сами :-).
Начнем с п.3, как наиболее простого.  

<sect3>3. Выдача данных клиенту<p>
Обычно клиенту выдают текст в формате HTML (ничто
не мешает Вам отправить ему и картинку/видео/etc). Для того, чтобы сервер и
клиент вас поняли, необходимо сказать, что вы выдаете, c помощью заголовка
Content-Type: mime-type/mime-subtype. Обратите внимание на регистр и
последовательность -- если вы скажите нечто типа Content_type, то сервер
вас скорее всего не поймет. (Сообщение типа &quot;500 Internal
Server Error&quot; будет симптомом).

Пример:
<tscreen><verb>
print "Content-Type: text/html\n";

# Мы выдаем текст в формате HTML. Также можно: text/plain -- простой текст, в
# браузере отобразится аналогично тексту, заключённому между тегами
# &lt;pre&gt;&lt;/pre&gt;.  image/gif -- Картинка, формат gif video/mpeg --
# mpeg-видео И целая куча других форматов, см.  файл mime.types из apache

print "\n"; 
# <-- еще одна пустая строка, обозначает конец вывода наших 
# заголовков. ВАЖНО!

# Теперь мы можем написать свой текст клиенту
print qq{
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;Моя первая CGI программа&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;h1&gt;Моя первая CGI программа&lt;/h1&gt;
&lt;/body&gt;
&lt;/head&gt;
};
</verb></tscreen>

<sect3>Прием данных от клиента<p>

Взаимодействие с клиентом обеспечивается так: Он заполняет форму своими
значениями, нажимает на кнопку "submit", броузер кодирует данные
соответствующим образом и отправляет их серверу.

<sect4>Определение форм<p>
Производится тегами &lt;form&gt; и &lt;/form&gt;. Тег определения форм имеет
следующие атрибуты<p>
<descrip>
<tag>action</tag>
скрипт на сервере, который будет запущен на сервере для обработки данных
формы.
<tag>method</tag> тип взаимодействия с сервером. Может иметь значения GET и
POST. Плюс, еще некоторые специальные, которые вы можете посмотреть в
rfc<p>
</descrip>
(Аттрибуты перечислены не все, как обычно)

Формы не могут быть вложенными.
 
Элементы ввода

Определяются тегами &lt;input&gt;, &lt;textarea&gt; и &lt;select&gt;
тег &lt;input&gt;:

<descrip>
<tag>&lt;input type=checkbox&gt;</tag> Элемент ввода &quot;Опция&quot;
<tag>&lt;input type=hidden&gt;</tag> Элемент ввода, который не виден
пользователю
<tag>&lt;input type=file&gt;</tag> позволяет загрузить файл на сервер
<tag>&lt;input type=image&gt;</tag>Изображение. Если по нему щелкнуть, это
вызовет submit формы и серверу будут выданы две переменные вида name.x и
name.y, где name -- то, что вы пропишете в name=... тега &lt;input&gt;
<tag>&lt;input type=password&gt;</tag>Элемент ввода Пароль. Позволяет ввести
строку, которая на экране отображается звездочками. Никаких методов защиты при
передаче на сервер не применяется
<tag>&lt;input type=radio&gt;</tag>Радиокнопки
<tag>&lt;input type=reset&gt;</tag>Кнопка сброса значений формы на начальные
<tag>&lt;input type=submit&gt;</tag>Кнопка отправки формы на сервер
<tag>&lt;input type=text&gt;</tag>строка ввода
</descrip>

Все теги &lt;input&gt; имеют атрибут name -- Наименование элемента. Служит для
идентификации при передаче на сервер, а также другие типа value, width, etc,
название и назначение которых можно опять-таки посмотреть в учебнике.

Тег &lt;textarea&gt; -- Поле многострочного ввода.<p>
Тег &lt;select&gt; -- списочный выбор<p>
 
Пример:
<tscreen><verb>
  &lt;form action=/cgi-bin/myscript.pl method=GET&gt;
  Имя: &lt;input type=string name=name&gt;&lt;p&gt;
  Пол: &lt;input type=radio name=gender value=male&gt;Мужской
       &lt;input type=radio name=gender value=female&gt;Женский
  &lt;input type=submit&gt;&lt;input type=reset&gt;
  &lt;/form&gt;
</verb></tscreen>

<sect3>Как это будет видно в моей программе?<p>
Это определяется методом формы, GET или POST

В случае GET сервер установит переменную окружения QUERY_STRING в виде
name1=vaue1&amp;name2=value2&amp;..&amp;nameN=valueN.<p>
В случае POST аналогичная строка будет записана на стандартный ввод. Ее
длину можно получить через переменную окружения CONTENT_LENGTH. <p>
В обоих случаях данные будут закодированы по следующему алгоритму:
<itemize>
<item>Если ASCII код символа больше 32 и меньше 128, то он будет выдан без
     изменения.
<item>если символ - пробел, то он заменится на "+" (плюсик, без кавычек)
  все остальное преобразуется в вид %xx, где xx -- шестнадцатеричный
     код символа.
</itemize>	 
<sect4>Декодирование на perl<p>
<tscreen><verb>
  tr/+/ /;
  s/%([0-9a-fA-F]{2})/pack("c",hex($1))/ge;
</verb></tscreen>  
  (списано из CGI.pm)<p>
     
На самом деле, можно не возится со всем этим, а использовать модуль CGI.pm
(Имеется в поставке Perl 5.004 и более поздних.  Если у вас perl версии 4
или 1, нужно срочно взять на CPAN новую версию perl)
<p>

<sect2>Самый большой и последний пример<p>
Предположим мы делаем поисковую систему, тогда у нас должен быть HTML с
формой и программа, которая будет выполнять поиск.

<tscreen><verb>search.html
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;Поиск&lt;/title&gt;
&lt;/head&gt;
&lt;&lt;body&gt;
&lt;h1&gt;Чего искать?&lt;/h1&gt;
&lt;form action=/cgi-bin/search.pl method=get&gt;
Строка для поиска: &lt;input type=text name=string width=70&gt;&lt;p&gt;
Искать в &lt;input type=checkbox name=searchin value=internet&gt;Интернет
  &lt;input type=checkbox name=searchin value=intranet&gt;Интранет
  &lt;input type=checkbox name=searchin value=extranet&gt;Экстранет&lt;p&gt;
&lt;input type=submit value=Давай!&gt;&lt;input type=reset value="Нет, не надо"&gt;
&lt;/form&gt;
&lt;/body&gt;
&lt;/html&gt;
</verb></tscreen>


Программа:
<tscreen><verb>
#!/usr/bin/perl -- поменяйте, как надо
use CGI qw(:standard); 

print "Content-Type: text/html\n\n"; # Не забудьте про два "\n"

$string = param("string");
@searchin = param("searchin"); # searchin это checkbox и его значения
             # возвращаются списком

# Искать мы сегодня ничего не будем
print "&lt;html&gt;
&lt;body&gt;
&lt;h1&gt;Мы сегодня ничего не ищем&lt;/h1&gt;
Но, если бы искали, то: &lt;br&gt;
Искали бы строку &lt;b&gt;$string&lt;/b&gt;&lt;br&gt;\n";
print "В &lt;b&gt;" . join(" ", @searchin) . "&lt;/b&gt;\n";
print "
&lt;/body&gt;
&lt;/html&gt;
";
</verb></tscreen>

Да, кстати, если вы собираетесь писать в файлы с помощью CGI-программ, не
забывайте про file locking (См. главу 8 за подробностями). <p>


<sect1>Как заставить браузеры не кешировать выдаваемый результат?<p>


Выдавайте заголовки в таком виде:
<verb>
print "Content-Type: type/sub-type\n"; # Подставьте Ваш тип/подтип
print "Pragma: no-cache\n"; # Для HTTP/1.0 клиентов
print "Cache-Control: no-cache\n"; # Для HTTP/1.1
print "Expires: Thu Jan  1 00:00:00 1970\n\n"; # Это уже любой броузер должен
					       # понять
</verb>

<sect1>Как перенаправить клиента на другой URL?<p>


В заголовке напишите: 	
<verb>	
print "Status: 302\n";
# Или 301. Разница состоит в том, что по стандарту 301 значит "перемещён
# навсегда", а 302 -- "перемещён временно"
print "Location: ВАШ Новый URL\n";
# URL должен быть указан абсолютный
print "URI: ВАШ новый URL\n\n"; # Для http/1.0
</verb>
Подробности: см. rfc1945(http/1.0),  rfc2068(http/1.1)

<sect1>Как загрузить файл на сервер?<p>

В поставку CGI.pm входит пример file_upload.cgi.
За подробностями смотрите CGI(3).

Учтите, что если у вас система, которая разделяет бинарные и текстовые файлы --
вам понадобится использовать binmode.

Также, помните, что &quot;Русский Apache&quot; не будет производить
перекодировку multipart форм.<p>

<sect1>Как получить файл по URL?<p>

Возьмите на CPAN библиотеку libwww-perl и смотрите lwpcook(3), там
написаны основы использования библиотеки и есть примеры.<p>
	
<sect1>Как использовать плюшки (cookies)?<p>
 
Плюшки работают следующим образом: программа CGI добавляет в свой
ответ директиву Set-Cookie, а клиент (если он поддерживает плюшки), при
следующих запросах возвращает их в заголовке Cookie. Сами плюшки
представляют из себя пары имя=значение. 

<sect2>Синтаксис Set-Cookie<p>
<verb>Set-Cookie: имя=значение; expires=дата; path=путь; domain=домен; secure</verb>
	<p>
Сервер может установить не более 20 плюшек, размер всей плюшки не может 
превышать 4Kb.<p>
<descrip>
	<tag>Имя=значение</tag>
	единственный обязательный параметр. И имя, и значение
	могут быть строками текста, не содержащими символов перевода строки,
	пробелов, ; и табуляции. Если необходимо их использовать, можно эти
	символы закодировать при помощи стандартного uri-кодирования.<p>

<tag>expires=дата</tag>
устанавливается дата истечения срока действия плюшки.
После этой дата плюшка клиентом не возвращается. Если дата не
установлена, то плюшка действует до завершения работы браузера.

<tag>path=путь</tag>
обозначает путь на сервере, для которого возвращается 
эта плюшка, если не указано, то только для того, который ее создал.

<tag>domain=домен</tag>
домен, для которого возвращается эта плюшка. Может
быть доменом уровня 2 и выше, т.е. .example.ru, а не .ru. Если не
установлен, то плюшка возвращается только серверу, который выдал
плюшку.

<tag>secure</tag>
указывает, что плюшка должна возвращаться только по
защищенному соединению (SSL).
</descrip>
	
<sect2>Синтаксис ответа клиента<p>
<verb>Cookie: имя1=значение1; имя2=значение2;....;имяn=значениеn</verb>
Программы CGI могут получить пары имя-значение через переменную
окружения HTTP_COOKIE. В модуле CGI.pm значение можно получить при
помощи метода cookie().

В деталях, механизм плюшек описан в rfc2109 (HTTP State Management 
Mechanism. D. Kristol, L. Montulli.  February 1997.)
	 
Пример:
1. Установка плюшки

<tscreen><verb>setcookie.pl
#!/usr/bin/perl 

print "Content-Type: text/html\n";
print "Set-Cookie: mycookie=some+string\n\n";
print "&lt;html&gt;&lt;head&gt;&lt;title&gt;Установка плюшки&lt;/title&gt;&lt;/head&gt;\n";
print "&lt;body&gt;&lt;h1&gt;Установка плюшки&lt;/h1&gt;\n";
print "&lt;a href=getcookie.pl&gt;Щелкни здесь,&lt;/a&gt; чтобы посмотреть, что
получилось.
print "&lt;/body&gt;&lt;/html&gt;";
</verb></tscreen>

2. Получение плюшки

<tscreen><verb>getcookie.pl;
#!/usr/bin/perl 
use CGI qw(:standard);

print "Content-Type: text/html\n\n";
print "&lt;html&gt;&lt;head&gt;&lt;title&gt;Получение плюшки&lt;/title&gt;&lt;/head&gt;\n";
print "&lt;body&gt;&lt;h1&gt;Плюшка&lt;/h1&gt;\n";
print "mycookie = ", cookie('mycookie');
print "&lt;/body&gt;&lt;/html&gt;";
</verb></tscreen>

<sect>Web серверы<label id="Web серверы"><p>

<sect1>Как заставить WWW сервер исполнять CGI-программы?<p>
<sect2>Apache для Unix/Win32<p>
Надо отредактировать конфигурационные файлы (я рассчитываю, что у вас default
конфиги apache)
<em>(NB: Apache/1.3.6 и выше по умолчанию конфигурируется только файлом httpd.conf.
Директивы все остались прежние, просто их слили в один файл)</em>

<sect3>Способ 1<p>

srm.conf

Директива <tt>ScriptAlias</tt>
<tscreen><verb>
ScriptAlias /cgi-bin/ /usr/local/apache/cgi-bin/
</verb></tscreen>
и в файле access.conf прописать<p>
<tscreen><verb>
&lt;Directory /usr/local/apache/cgi-bin/&gt;
Options ExecCGI 
&lt;/Directory&gt; 
</verb></tscreen>
(если пригляделся, там нужно только раскоментировать опции) 
Это позволит вам помещать программы в каталог
/usr/local/apache/cgi-bin/ и они будут видны по URL
http://you/cgi-bin/program_name

<sect3>Способ 2<p>
Прописать 

<tscreen><verb>
<Files *.cgi>
	SetHandler cgi-script
	Options ExecCGI
&lt;/Files>
</verb></tscreen>

и apache буде исполнять файлы с расширением .cgi из любого каталога как 
CGI-скрипты.

<sect1>Мой CGI-скрипт не работает! Как установить причину?<p>

Прежде чем читать дальше, убедитесь в том, что:
<itemize>
<item>Вы запустили скрипт с командной строки (не через CGI!!!) и он работает, а
не говорит &quot;compilation stopped due to errors&quot;
<item>Скрипт не только работает, но и выдает нужный заголовок с пустой строкой
после него
<item>Для многопользовательских систем) Вы сделали тоже под пользователем, под
которым работает сервер (su -m имя_пользователя -c script в Unix) и он столь же
благополучно работает
</itemize><p>

<sect1>Ошибки HTTP 500 и 403. Что это значит и что с ними делать<p>
error_log для того и служит, чтобы туда смотреть :-)

<sect2>Ошибка 403 &quot;Forbidden&quot;<p>
Возникает, если сервер не может отдать вам содержимое по причине отсутствия
полномочий. 

Проверьте:
<itemize>
<item> Может ли user (для apache под Unix это обычно nobody), под которым работает,  сервер исполнять файл? 
<item> В частности, может ли сервер исполнять интерпретатор (в случае
скриптовых языков)?
<item> Можно ли для этого каталога исполнять программы CGI? (Options ExecCGI для
apache) 
</itemize>

<sect2>Ошибка 500 &quot;Internal Server Error&quot;<p>
Возникает в случае внутренней ошибки.

Проверьте:
<itemize>
<item>Выводите ли вы строку "Content-Type:"?
<item>Правильно ли вы ее выводите? (Content-type, Content_Type --
неправильно)
<item>Заканчиваете ли вы хедеры пустой строкой? Нельзя написать 
<tt>
print "Content-Type: text/html\n";
print "&lt;HTML&gt;\n";
</tt>
Надо писать:
<tt>
print "Content-Type: text/html\n\n"; # Два "\n"
print "&lt;HTML&gt;\n";
</tt>
</itemize>

  Также, эта ошибка возникает, если CGI-программа завершилась с ненулевым
кодом возврата, что часто встречается в случае некомпилируемости вашего
скрипта perl'ом.<p>

Совет: делайте 
<tt>use CGI::Carp qw(fatalsToBrowser)</tt>
во время отладки, тогда вам выдадут сообщение об ошибке perl.<p>

<sect1>Почему $ENV{REMOTE_HOST} пуста?<p>

	apache устанавливает переменную окружения REMOTE_HOST, если ему
разрешено проводить dns запросы для определения имени. Для 1.3.x
по-умолчанию это выключено. Включается/выключается директивой
HostNameLookups, которая может принимать 3 значения: on -- проводить
запросы, off -- не запрашивать dns и double -- делать двойные запросы:
запрашивать имя хоста, а затем по имени запрашивать ip адрес, для
безопасности.<p>
	
<sect1>Почему $ENV{REMOTE_USER} пуста?<p>

	Переменная окружения REMOTE_USER устанавливается apache в случае, если
скрипт защищен паролем. Как это сделать рассказано на
<url url="http://www.apacheweek.com/features/userauth" name="http://www.apacheweek.com/features/userauth"> и 
<url url="http://www.apacheweek.com/features/dbmauth" name="http://www.apacheweek.com/features/dbmauth"><p>

<sect1>Как получить пароль при http-авторизации?<p>

Прописать 
<tscreen><verb>
RewriteEngine   On
RewriteCond     %{HTTP:Authorization}   ^(.*)$ [NC]
RewriteRule     /.*             -       [E=HTTP_AUTHORIZATION:%1] 
</verb></tscreen>

в конфиг апача (Должен быть включен mod_rewite) и apache будет выставлять 
в окружение переменную $ENV{HTTP_AUTHORIZATION}. <p>

Пример как ее декодировать:
<tscreen><verb>
#!/usr/bin/perl -w
use strict;
use MIME::Base64;

print "Content-Type: text/plain; charset=koi8-r\n\n";
# В $ENV{HTTP_AUTHORIZATION} сейчас нечто типа: 
# Basic YXBhdmVsOmZvb2Jhcg==

my ($type, $encoded) = split(/ /, $ENV{HTTP_AUTHORIZATION});
my ($login, $passw) = split(/:/, decode_base64($encoded));

print 
"Type: $type
Login: $login
Password: $passw
";
</verb></tscreen>

Учтите, что на некоторых системах, чужие юзеры могут заглядывать в
environment процесса и могут таким образом узнать пароли.

<sect>Ускоряемся<label id="Ускоряемся"><p>
<sect1>Моя CGI программа работает слишком медленно/создает большую нагрузку на сервер/etc.  Что делать?<p>

<itemize>
<item> Вкомплировать интерпретатор в сервер (mod_dtcl, mod_perl, pyapache для apache, 
серверы с поддержкой servlets, AOL server с его поддержкой tcl).
<item> Переделать на FastCGI или аналогичную технологию.
<item> Переписать ее на компилируемый язык, если написано на скриптовом.  Тем самым
вы избавитесь от оверхедов интерпретатора.
<item> Взять более мощный сервер (IBM RS/6k F50, к примеру :-)
</itemize>
<p>

<sect1>Что такое mod_perl?<p>

Это perl, вкомпилированный в apache, что придает многие преимущества:
<itemize>
<item>Скрипты и модули компилируются один раз, что значительно увеличивает 
	скорость ответа на запрос HTTP (до 2000%);
<item>Можно написать свои обработчики различных стадий запроса;
<item>Кеширование соединений с сервером БД -- еще прибавка к скорости;
<item>Директива SSI #perl позволяет в server-parsed html вызывать функции perl;
<item>секции &lt;perl&gt;&lt;/perl&gt; в конфиге apache, для его конфигурирования;
</itemize>	

Правда, ничего не дается даром и самый важный недостаток mod_perl --
<em>огромный</em> расход памяти: если обычный apache занимает при работе обычно
меньше мегабайта, то apache с mod_perl размером в 10-15 Mb -- вполне нормальное
явление. Но, при правильной настройке, значительная часть этой памяти будет
shared между копиями процессов.

Основной сайт про mod_perl - <url url="http://perl.apache.org" name="http://perl.apache.org">, в частности
великолепный mod_perl Guide на <url url="http://perl.apache.org/guide/" name="http://perl.apache.org/guide/">.
Новости mod_perl на сайте <url url="http://www.take23.org" name="Take23.org">
<p>

<sect1>В чем разница между скриптом для CGI и mod_perl?<p>

	Программа CGI исполняется один раз, за тем умирает. Под mod_perl же она загружается и
исполняется до смерти сервера (в случае использования Apache::Registry, см.
далее), поэтому она требует более аккуратного описания - закрывать файлы, не 
использовать много памяти, и тд.

Более подробно о переписывании программ CGI под mod_perl - 
<url url="http://perl.apache.org/guide/porting.html" name="http://perl.apache.org/guide/porting.html">.

<sect1>В чем разница между исполнением под Apache::Registry и Apache::PerlRun?<p><p>

<sect1>Как использовать постоянное соединение с БД под mod_perl?<p>

<sect1>Что такое FastCGI?<p>


<sect>Использование других языков для написания CGI-программ<label id="Использование других языков для написания CGI-программ"><p>

<sect1>Можно ли писать CGI-программы не на Perl?<p>

Да, можно.  CGI не зависит от используемого языка программирования.
	
<sect1>Какие существуют библиотеки на других языках для CGI?<p>

Это далеко неполный список. (Почти полностью списано из CGI_metaFAQ от 
brian d foy).

<sect2>C/C++<p>
<itemize>
<item><url url="http://www.boutell.com/cgic/" name="Tom Boutell's cgic"> (C library) &lt;URL:http://www.boutell.com/cgic/&gt;
<item><url url="http://www.eekim.com/software/cgihtml/" name="Eugene Eric Kim's cgihtml"> (C library) &lt;URL:http://www.eekim.com/software/cgihtml/&gt;
<item><url url="http://www.camtech.com.au/jemtek/cgi/itemb/" name="JemTek's (C Library)"> &lt;URL:http://www.camtech.com.au/jemtek/cgi/itemb/&gt;
<item><url url="http://www3.pair.com/webthing/cgiplusplus/" name="Nick Kew's CGI++ (C++ library)"> &lt;URL:http://www3.pair.com/webthing/cgiplusplus/&gt;
<item><url url="http://www.cs.hmc.edu/~sbooth/cgicc/" name="Stephen F. Booth's Cgicc (C++ library)"> &lt;URL:http://www.cs.hmc.edu/~sbooth/cgicc/&gt;
<item><url url="http://www1.dpe.net/C++/" name="S. Martin's CGI.h C++ library"> &lt;http://www1.dpe.net/C++/&gt;
</itemize><p>

<sect2>Haskell<p>
<itemize>
<item><url url="http://www.pms.informatik.uni-muenchen.de/mitarbeiter/panne/haskell_itembs/CGI.html" name="http://www.pms.informatik.uni-muenchen.de/mitarbeiter/panne/haskell_itembs/CGI.html">
<item><url url="http://www.cse.ogi.edu/~erik/Personal/cgi.htm" name="http://www.cse.ogi.edu/~erik/Personal/cgi.htm">
</itemize>
<em>По поводу Haskell <url url="http://wave.philol.msu.ru/fp/haskell/" name="http://wave.philol.msu.ru/fp/haskell/"></em>
<sect2>Phyton<p>
<sect2>rexx<p>
<sect2>Ada<p>

<sect2>Java<p>
<itemize>
<item>Java CGI package <url url="ftp://ftp.orbits.com/pub/software/" name="ftp://ftp.orbits.com/pub/software/">.
Лучше использовать сервлеты:
<itemize>
<item><url url="http://www.servletscentral.org" name="http://www.servletscentral.org">
<item><url url="http://javasoft.com/products/servlet/" name="http://javasoft.com/products/servlet">
<item><url url="http://java.apache.org" name="http://java.apache.org">
</itemize>
</itemize>


<sect>БД на плоских файлах<label id="БД на плоских файлах"><p>
<sect1>Я хочу какую-нибудь простейшую БД и прямо сейчас!<p>

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

Файл с данными может выглядеть так:

<tscreen><verb>phones.data
Иванов И.И.::888-0000::Какая-то улица, 17, кв 40
Сидоров П.И.::888-8429::Другая улица, 5, кв 21
...... и тд.
</verb></tscreen>

тогда программа, которая читает данные, может быть примерно такого вида:

<tscreen><verb>dump_phones.pl
#!/usr/bin/perl
$filename = 'phones.data';
# открываем файл
open DATA, $filename or die "Невозможно открыть $filename: $!";

# читаем построчно из файла
while (&lt;DATA&gt;) {
	chomp; # удаление символа конца строки
	# теперь в $_ есть строка и мы ее разделяем на переменные
	($name, $phone, $address) = split(/::/);
	# и выведем на печать
	print "Имя: $name, телефон: $phone, адрес: $address\n";
}
close DATA;
</verb></tscreen>

Больше проблем возникает в случае, если надо удалить или отредактировать
запись, но и их можно довольно просто и элегантно решить, если использовать
механизм редактирования на месте (inplace edit) -- при использовании операции
"ромб"(&lt;&gt;), можно читать из одного файла, а писать в другой:

<tscreen><verb>change_phones.pl
#!/usr/bin/perl 
$^I = '~'; # запускаем inplace edit
while (<>) { # Обратите внимание, что мы не открывали файл: при такой
	#конструкции имя файла берется из командной строки
	chomp;
	($name, $phone, $address) = split(/::/);
	if (.... некоторое условие, при котором мы оставляем наши данные ... )
	{
		print "$name::$phone::$address\n"; # теперь данные есть в новом файле
	}
}
</verb></tscreen>

если запустить это программу как
<verb>change_phones.pl phones.data,</verb>
то в текущем каталоге будут два файла: phones.data, с записями, которые
удовлетворили нашим условиям и phones.data&tilde; -- предыдущая копия.

Также, во многих случаях, всю программу такого типа можно записать как
one-liner: <verb>perl -i~ -n -e 'print if(... условие)'</verb>
<sect2>Файлы CSV<p>
Схема хранения данных, использованная в предыдущем примере весьма удобна,
но страдает одним недостатком -- в значениях не может использоватся 
разделитель, или его приходится специальным образом обрабатывать при чтении и 
записи таких файлов.

Text::CSV именно это и делает, позволяя оперировать файлами в формате 
CSV (comma-separated values).

Например, записывать:
<tscreen><verb>
#!/usr/bin/perl -w
use strict;
use Text::CSV_XS;

my ($name, $phone, $address) = ('ТОО "Рога и Копыта"', '888-00-11', 'Какая-то улица, 17');

my $csv = Text::CSV_XS->new({
	# надо для русских букв
	'binary'=>1
	});
$csv->combine($name, $phone, $address) or die "Combine failed";

print $csv->string();
# Выведет "ТОО ""Рога и Копыта""",888-0000,"Какая-то улица, 17"
</verb></tscreen>

и читать
<tscreen><verb>
#!/usr/bin/perl -w
use strict;
use Text::CSV_XS;

my $csv = Text::CSV_XS->new({
	'binary'=>1
	});

open F, "<phones.csv" or die "phones.csv: $!";

my $line_num = 1;
# Читаем по одной строке из файла
while (my $line = <F>) {
	# Пробуем разобрать на поля.  parse() возвращает 1, если все прошло ok
	# и 0, если строка ему не понравилась.
	if ($csv->parse($line)) {
		# fields возвращает значения
		my ($name, $phone, $address) = $csv->fields();
		printf "%-30s %-15s %-30s\n", $name, $phone, $address;
	} else {
		# error_input вернет неправильную строку.
		die "Неправильная строка #$line_num: ", $csv->error_input();
	}	
	$line_num++;
}
close F;
</verb></tscreen>	

<tscreen><verb>phones.csv
"ТОО ""Рога и Копыта""",888-0000,"Широкий бульвар, 4"
"Петров А.А.",887-00-00,"Длинный проспект, 124"
</verb></tscreen>
<sect2>Двоичные файлы<p>

Для чтения двоичных файлов в Perl можно использовать функции read и unpack.
К примеру, если использовать двоичный файл для хранения телефонной книги такого
формата:<p>

	40 символов -- фамилия, И.О. <p>
	10 символов -- номер телефона, <p>
	60 символов -- адрес,<p>
	
то строка описания формата для unpack будет выглядеть так:
<verb>$format_str = 'A40 A10 A60'</verb>, а сама программа, аналогичная 
первому примеру:
<tscreen><verb>binary_phones.pl
#!/usr/bin/perl
$format_str = 'A40 A10 A60';
open DATA, 'binary.dat' or die "$!";
while (read(DATA, $buf, 40+10+60)) { # <DATA> не покатит: такая
# конструкция будет читать до символа перевода строки, а это не то, что нужно
	($name, $phone, $address) = unpack($format_str, $buf);
	# Теперь в $name, $phone, $address есть данные и с ними можно делать
	# все, что захочется
}
close DATA;
</verb></tscreen>

Чтобы вывести в файл такую запись можно использовать конструкцию типа <p>
<tt>
print FILE pack($format_str, $name, $phone, $address);
</tt>

<sect1>Можно ли как-нибудь из Perl получить доступ к dbf файлам?<p>

Да, можно. На <url url="http://www.fi.muni.cz/~adelton/" name="http://www.fi.muni.cz/~adelton/"> есть модуль XBase, который
позволяет читать/писать dbf. При чтении он даже поддерживает индексы.
Кроме того, в комплект поставки также входит модуль DBD::XBase, при помощи
которого можно оперировать dbf на SQL (более подробно про DBI -- далее).

<sect1>А к MS access .mdb?<p>

К файлам MS Access нельзя обращаться из perl напрямую.

К MS Access можно обращаться по ODBC, при помощи DBD::ODBC.

<sect1>Зачем и как нужно запирать (lock) файлы?<p> 

Представьте себе ситуацию когда одновременно работают несколько копий одной и
той же программы (к примеру, cgi-скрипты, обслуживающие запросы),
читающие/пишущие в один файл, тогда рано или поздно возникнет ситуация при
которой один скрипт прочитал данные, произвел над ними некоторые действия и
собрался записать их назад в файл, но в это же время другой скрипт тоже
прочитал данные, тоже произвел над ними действия, но (!) он прочитал старые
данные, которые он и запишет поверх данных, выданных другим скриптом. Таким
образом, в файле останутся данные записанные одним из скриптов -- в лучшем
случае, в худшем -- структура файла будет испорчена.  Чтобы этого избежать в
Unix и большинстве других ОС есть системный вызов flock(2) или
аналогичный.

<sect2>Как использовать flock<p>
К примеру, скрипт который записывает имена вызывающих хостов в файл. (На деле
такой список, конечно, можно получить из журнала регистрации web-сервера).
<tscreen><verb>lock_exm.pl
#!/usr/bin/perl
use Fcntl; # Импорт констант
open (HOSTS, '>>hosts.log'); # Файл открыт для добавления записи
flock(HOSTS, LOCK_EX); 
# Теперь файл заблокирован: Если любой другой скрипт тоже вызовет flock на
# этом файле, его flock не вернет управление в программу, пока мы не
# разблокируем файл. Обратите внимание: flock -- декларативная функция, если
# один из скриптов ее не использует при записи, то вся ваша блокировка не
# работает.
print HOSTS $ENV{REMOTE_HOST}, "\n"; # записали строку
close HOSTS; # Файл при закрытии разблокируется автоматически -- unlock вызывать не надо.

# Вывести сообщение для пользователей
print "Content-Type: text/plain\n\n";
print "Название вашего хоста записано\n";
</verb></tscreen>

Более подробный рассказ о flock и пример доступны на
<url url="http://w3.stonehenge.com/merlyn/WebTechniques/col04.html" name="http://w3.stonehenge.com/merlyn/WebTechniques/col04.html">

<sect1>Чего делать на системах где flock() нет?<p>

Судя по perlfaq5(1), можно использовать модуль File::Lock с CPAN.

<sect>Реляционные, пост-реляционные и объектно-реляционные СУБД<label id="Реляционные, пост-реляционные и объектно-реляционные СУБД"><p>

<sect1>Как использовать DBI?<p>

<sect2>Введение<p>
DBI -- это интерфейс прикладных программ к СУБД, использующим SQL в качестве
языка запросов.  Сам DBI определяет только набор функций, переменных и
соглашений. Вся непосредственная работа выполняется Database Drivers (DBD) --
модулями, обеспечивающими связь с СУБД. DBI только обеспечивает стандартный
интерфейс для этих драйверов.

Полная схема архитектуры при работе DBI выглядит примерно так:
<tscreen><verb>
+----------------------+
| Прикладная программа |
+----------------------+
| DBI                  |
+----------------------+
| DBD                  |
+----------------------+
| СУБД                 |
+----------------------+
</verb></tscreen>

<sect2>Подсоединение к СУБД<p>

обеспечивается при помощи метода connect класса DBI:

<verb>$dbh = DBI->connect($dsn, $user, $auth, {options});</verb>

<descrip>
<tag>$dbh</tag>Это объект, при помощи его методов осуществляются взаимодействия с
	СУБД.
<tag>$dsn</tag>Строка, определяющая к какой базе данных подсоединятся и другие
	параметры. Зависит от DBD. На сегодняшний момент стандарта нет, но
	рекомендовано использовать стиль ODBC:
	
	<verb>dbi:&lt;имя DBD&gt;:databasename=&lt;название БД&gt;;host=&lt;Имя хоста&gt;;port=&lt;порт&gt;</verb>
<tag>$user</tag>Имя пользователя.

<tag>$auth</tag>Нечто, авторизующее пользователя. Обычно пароль.

<tag>options</tag>Параметры DBI, передаются через анонимный хеш. В настоящее время
	понимаются три параметра: 
	<descrip>
		<tag>RaiseError</tag>если установлен, то при любой ошибке DBI убивает 
		программу 
		<tag>PrintError</tag>если установлен, то при ошибке DBI вызывает warn
		<tag>AutoCommit</tag>определяет порядок работы с транзакциями.
	</descrip>	
</descrip>

Например:
<verb>$dbh = DBI->connect('dbi:Pg:dbname=apavel', 'apavel', 'SomeSecret',
{RaiseError=>1, AutoCommit=>0});</verb>
Означает: Подсоедение к СУБД PostgreSQL, к базе данных apavel, с именем
пользователя apavel и паролем. Все ошибки будут вызывать die, что удобно при
отладке, а все изменения будут внесены только при подтверждении (commit)
транзакций.<p>

Отсоединение обеспечивается при помощи метода disconnect: $dbh->disconnect();
	
<sect2>Механизм курсоров и подготовки запросов<p>

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

Пример таблицы, используемый в дальнейшем:
<tscreen><verb>foo.sql
create table foo (
	bar varchar(50),
	baz int
)	
</verb></tscreen>

<sect3>Получение данных<p>
<tscreen><verb>
$cursor = $dbh->prepare('select bar, baz from foo');
# теперь $cursor -- курсор, и его необходимо исполнить
$cursor->execute;
# После исполнения запроса, результат можно получить из курсора при помощи
# метода fetchrow_array
while (($bar, $baz) = $cursor->fetchrow_array) {
	print "bar is: $bar, baz: $baz\n";
}	
</verb></tscreen>

<sect3>Placeholders<p>

Очень часто бывает надо подготовить какой-либо запрос, а потом использовать
его с разными значениями данных. DBI предлагает для механизм placeholders: 
В запросе на месте таких данных указываются вопросительные знаки, а сами
значения передаются в метод execute() курсора. Например:
<verb>$cursor = $dbh->prepare('select bar from foo where baz=?');
$cursor->execute($baz);</verb>
Особенно удобно это в случае вставки данных:
<verb>$cursor = $dbh->prepare('insert into foo(bar, baz) values(?, ?)');
while ( ... ) {
	$cursor->execute($bar, $baz);
}</verb>

Таким образом, СУБД разбирает запрос только один раз, а затем просто
исполняет его, что экономит время. (Естественно, это верно только для DBMS с
раздельными parse и execute, сейчас ни MySQL, ни PostgreSQL такое не
поддерживают, поэтому их реализации DBD просто сохраняют запрос переданный
$dbh->prepare() и затем подставляют в него данные при каждом $sth->execute().)

<sect2>Работа без курсоров<p>
DBI предоставляет несколько методов для такого рода работы:
	Методы для запросов:<descrip>
		<tag>selectrow_array</tag> Возвращает одну строку запроса в виде
		массива
		<tag>selectall_arrayref</tag>Возвращает весь ответ сервера в виде
		массива ссылок на массивы.
		</descrip>
	Методы для выражений, не возвращающих значений
	<descrip>
		<tag>do</tag>исполняет запрос
	</descrip>	
	
Пример:
<tscreen><verb>
#получить значение bar при baz=3
($bar) = $dbh->selectrow_array('select bar from foo where baz=3');

# установить baz в некоторое значение при bar='somestring'
$dbh->do("update set baz=1 where bar='somestring'");
</verb></tscreen>
<p>

<sect1>Как работать с записями кусочками: первые N записей, следующие N...?<p>	
Можно несколькими способами:
1. Просто прокручивая курсор:
<tscreen><verb>
	$c = $dbh->prepare('select baz, bar from foo');
	$c->execute;
	# если нужна последовательность с 26 по 50

	for ($k = 0; $k < 26; $k++) {
		$c->fetchrow_array; 
	}
	# теперь можно вывести данные
	print "&lt;table border=1&gt;&lt;tr&gt;&lt;th&gt;bar&lt;/th&gt;&lt;th&gt;baz&lt;/th&gt;&lt;/tr&gt;\n";
	while (($bar, $baz) = $c->fetchtrow_array) {
		print "&lt;tr&gt;&lt;td&gt;$bar&lt;/td&gt;&lt;td&gt;$baz&lt;/td&gt;&lt;/tr&gt;\n";
	}
	$c->finish; # Закрыть курсор
	print "&lt;/table&gt;";
</verb></tscreen>

2. Используя курсоры СУБД
<tscreen><verb>
	# Показан синтаксис PostgeSQL
	$dbh->do('declare mycursor cursor for select bar, baz from foo');
	$dbh->do('move 25');

	# И теперь будем получать данные
	$c = $dbh->prepare('fetch forward 25 in mycursor');
	while (($bar, $baz) = $c->fetchrow_array) {
		print ....;
	}
	$c->finish;
	$dbh->do('close mycursor');
</verb></tscreen>

3. Для MySQL можно использовать директиву LIMIT
<tscreen><verb>
	$c = $dbh->prepare('select bar, baz from foo limit 26,25');
	while (($bar, $baz) = $c->fetchrow_array) {
		print ....;
	}
	$c->finish;
</verb></tscreen>	

<sect1>Где взять документацию/учебник по SQL?<p>

Мартин Грабер, &quot;Введение в SQL&quot;, тем более, что недавно вышло новое 
издание на русском языке.

<sect1>Как можно подсоединится к MS SQL Server?<p>

Вроде как можно при помощи DBD::FreeTDS

<sect>Где взять perl для m$ windows?<label id="Где взять perl для m$ windows?"><p>

<sect>Борьба с глюками ms windows<label id="Борьба с глюками ms windows"><p>

<sect1>Почему мой perl ругается на flock()?<p>

Потому что на windows 9x нет flock(2).

Используйте File::Lock с CPAN.

<sect1>Тоже самое, но с fork()?<p>

perl, входящий в <url url="http://sources.redhat.com/cygwin/" name="Cygwin">
-- набор утилит unix для windows, умеет эмуляцию fork.

<sect1>Как бы мне добится нормальной сортировки в алфавитном порядке, преобразования в прописные/строчные буквы?<p>

Perl для этого опирается на механизм locale.  К счастью, это работает и на
windows:

Если надо работать с CP866,
пишем 
<verb>
  use locale;
  use POSIX;
  &amp;POSIX::setlocale(&amp;POSIX::LC_ALL, "Russian_Russia.866");
  print uc "Да, здесь будут заглавные буквы";
</verb>

если KOI8 (sic!):
<verb>
  &amp;POSIX::setlocale(&amp;POSIX::LC_ALL, "Russian_Russia.20866");
</verb>

если с CP1251 -- просто

<verb>use locale;</verb>

Со всеми тремя кодировками работают и uc/lc, и /\w/

Проверено под NT на perl 5.005_02, собранном через VC++ 5.0
из исходников, скачанных с CPAN/ports/win32/Standard/

<sect1>Почему скрипты, которые я написал дома на windows, при загрузке на unix машину не работают?<p>

Этому может быть миллион разных причин, но самая
часто встречающаяся -- unix и наследники CP/M
используют разные последовательности конца строки.

Если вы их загружаете по ftp, включите режим ASCII. 

<sect1>Я включил на windows локаль в своих сриптах, но на Unix-сервере она не работает<p>

Названия локалей на windows и unix системах различны.  Локаль KOI8-R на
unix скорее всего будет называтся ru_RU.KOI8-R, CP1251 может называтся 
по разному, спросите у администратора сервера.

<sect>Прочее, не относящие к perl и web<label id="прочее, не относящие к perl и web"><p>

<sect1>Как сделать чтоб моя программа автоматически запускалась в заданное время?<p>

<sect2>Unix<p>

Используйте стандартный демон <tt>cron.</tt> 

см. crontab(1)

<sect2>Прочие платформы<p>

Смотрите документацию на свои ОС.

<sect>Авторы и копирайт<label id="Авторы и копирайт"><p>

<sect1>Авторы ответов<p>

<itemize>
<item>Павел Аммосов 
<item>Артур Пенттинен
<item>Михаил Поляков
<item>Григорий Строкин
</itemize>

<sect1>Копирайт<p>

Этот текст &copy; группа авторов.  Разрешается его распространение электронным или
печатным образом, частично или полностью, если: сохранены все сообщения об
авторстве, текст не изменялся (за исключением форматирования) и предпринимались 
достаточные попытки использовать наиболее современную версию.


</article>

