Отправляет email-рассылки с помощью сервиса Sendsay
  Все выпуски  

Программирование скриптов на Perl'e (Выпуск №4)


Служба Рассылок Subscribe.Ru проекта Citycat.Ru

Программирование скриптов на Perl'e (выпуск 4).


Здравствуйте все, кто читает эту рассылку.



Сегодня в выпуске:




Несколько слов перед началом

Ух, ну вы, ребята, и даете. После выхода третьего номера рассылки я, честно говоря, думал, что у меня почтовый ящик лопнет от ваших писем с поздравлениями.   Огромное спасибо всем!!!   Мне было очень приятно.

Заодно прошу прощения за затянувшийся перерыв в выпуске рассылки, надеюсь, что Вы, мои подписчики, не очень устали ее ждать.

Здесь же хочу ответить всем тем, кто просил меня указать адрес, по которому располагается крэк для Perl Builder 2.0.   К сожалению, я не могу указать этот адрес в своей рассылке, так как это противоречит "Правилам ведения рассылок на subscribe.ru".   Пункт, касающийся этого, выглядит так:

   2. Категорически запрещается:
     2) Публиковать в рассылке материалы порнографического характера,
        crack/hack материалы, а также ссылки на сайты, содержащие
        подобную информацию.
Поэтому я, как Вы понимаете, не могу дать эту ссылку.   Единственное, что я могу порекомендовать всем ищущим крэк для Perl Builder 2.0, так это попытаться найти его через поисковый портал Asta la Vista, по адресу: http://www.astalavista.box.sk

Ко всему вышесказанному хочу еще добавить пару слов.   Я пришел к выводу, что многие читатели рассылки в какой-то мере знакомы с Perl'ом и им, полагаю, не особенно интересно базовое описание этого языка.   И поэтому я решил начать вести в рассылке одновременно два раздела - один для тех, кто не знаком с Perl'ом и желает его изучить, а второй будет непосредственно посвящен CGI-программированию на Perl'е.   В первом разделе я продолжу для начинающих поэтапное обучение языку Perl, а во втором начну знакомить Вас, дорогие мои подписчики, с принципами и приемами программирования CGI-скриптов.

Ну а теперь перейдем к делу.   Темы сегодняшнего выпуска:




Perl для начинающих - Операторы языка Perl

Сегодня мы рассмотрим операторы условий и циклов.

Условия

В Perl'е, в отличие от других языков программирования, где для проверки условий используются конструкции типа:
        if (УСЛОВИЕ)
          {  БЛОК
          }

или

        if (УСЛОВИЕ)
          {  БЛОК1
          }
        else
          {  БЛОК2
          }
существуют также проверки условий вида:
        ОПЕРАТОР if УСЛОВИЕ;
        ОПЕРАТОР unless УСЛОВИЕ;

где УСЛОВИЕ - выражение, возвращающее логическое значение "истина" или "ложь".

Рассмотрим теперь более подробно, как работает каждый из этих операторов.

  • ОПЕРАТОР  if  УСЛОВИЕ;   -   Проверка "если".   ОПЕРАТОР выполняется, если УСЛОВИЕ возвращает "истина".
        Пример:
            $var=1;
            $var2=3 if $var>0;    # Результат: $var2=3
    
  • ОПЕРАТОР  unless  УСЛОВИЕ;   -   Проверка "если не".   Обратная к if.   ОПЕРАТОР выполняется, если УСЛОВИЕ возвращает "ложь".
        Пример:
            $var=1;
            print ++$var unless $var>5;    # Печать $var с инкрементом
        Результат: 2
    
    Эти операторы условий удобно использовать, когда от результата проверки зависит только одно действие, т. к. можно обойтись без заключения этого действия в операторные скобки, что позволяет хоть ненамного, но все-таки уменьшить размер программы.

    Но естественно в Perl'е, как и в других языках программирования, существуют и традиционные конструкции проверки условий:

            if (УСЛОВИЕ)
              {  БЛОК
              }
    
    и
    
            if (УСЛОВИЕ)
              {  БЛОК1
              }
            else
              {  БЛОК2
              }
    
    но, кроме этого, в Perl'е существует еще одна, поистине уникальная, конструкция проверки условий:
            if (УСЛОВИЕ1)
              {  БЛОК1
              }
            elsif (УСЛОВИЕ2)
              {  БЛОК2
              }
            ...
            else
              {  БЛОКN
              }
    
    Рассмотрим теперь более подробно, как работает каждый из этих операторов.

    Самый простой тип оператора проверки условия:

            if (УСЛОВИЕ)
              {  БЛОК
              }
    
    При использовании такой конструкции происходит следующее:  вычисляется логическое выражение УСЛОВИЕ и если результат "истина", то блок операторов выполняется.
        Пример:
            $var=1;
            if ($var==1)
              {  print $var,"\n";
              }
        Результат: 1
    
    Следующий оператор проверки отличается от предыдущего тем, что у него есть альтернативный блок операторов:
            if (УСЛОВИЕ)
              {  БЛОК1
              }
            else
              {  БЛОК2
              }
    
    Если используется такая конструкция, то последовательность действий такая:  если УСЛОВИЕ="истина" выполняется БЛОК1 иначе БЛОК2.
        Пример:
            $var=2;
            if ($var==1)
              {  print "\$var=1\n";
              }
            else
              {  print "\$var не равно 1\n";
              }
        Результат: $var не равно 1
    
    Ну и наконец последняя и самая интересная конструкция:
            if (УСЛОВИЕ1)
              {  БЛОК1
              }
            elsif (УСЛОВИЕ2)
              {  БЛОК2
              }
            ...
            else
              {  БЛОКN
              }
    
    При использовании такой конструкции проверки мы можем проверять в одном операторе проверки большое количество условий, что может пригодиться при построении аналога оператора выбора (case для Паскаля и switch для Си), т. к. этот оператор, к сожалению, в Perl'е отсутствует.   Работает эта конструкция следующим образом:  если УСЛОВИЕ1="истина" выполняется БЛОК1 иначе если УСЛОВИЕ2="истина" выполняется БЛОК2 иначе ... иначе выполняется БЛОКN.
        Пример:
            $var=1;
            if ($var==0)
              {  print "\$var=0\n";
              }
            elsif ($var==1)
              { print "\$var=1\n";
              }
            else
              { print "Не известное \$var\n";
              }
        Результат: $var=1
    

    Циклы

    Также как и среди операторов проверки условий среди операторов циклов существуют конструкции отсутствующие в других языках:
            ОПЕРАТОР while УСЛОВИЕ;
            ОПЕРАТОР until УСЛОВИЕ;
    
    где УСЛОВИЕ - выражение, возвращающее логическое значение "истина" или "ложь".
    
    Рассмотрим их более подробно.

  • ОПЕРАТОР  while  УСЛОВИЕ;   -   Проверка "пока".   ОПЕРАТОР выполняется столько раз, пока УСЛОВИЕ="истина".
        Пример:
            $var=1;
            print $var++ while $var<5;    # Печать $var с инкрементом
        Результат: 1234
    
  • ОПЕРАТОР  until  УСЛОВИЕ;   -   Проверка "до".   ОПЕРАТОР выполняется до тех пор, пока УСЛОВИЕ="ложь".
        Пример:
            $var=5;
            print $var-- until $var<1;   # Печать $var с декрементом
        Результат: 54321
    
    Оба вышеприведенных цикла работают, как Вы понимаете, только с одним оператором.   Для выполнения блока операторов в Perl'е существуют следующие типы циклов:
            while (УСЛОВИЕ)
              {  БЛОК
              }
    или
            while (УСЛОВИЕ)
              {  БЛОК
              }
            continue
              {  БЛОК
              }
    
            for (УСЛОВИЕ1; УСЛОВИЕ2; УСЛОВИЕ3)
              {  БЛОК
              }
    
    и, кроме того, еще один цикл, отсутствующий в других языках:
    
            foreach ПЕРЕМЕННАЯ (СПИСОК)
              {  БЛОК
              }
    
    Но перед тем как перейти к подробному описанию этих операторов цикла рассмотрим операторы управления циклом.

    next   -   подобен оператору continue в Си.   Переходит к началу текущего цикла, т. е. повторяет итерацию.
    last   -   подобен оператору break в языке Си.   Немедленно прерывает цикл.   Блок continue (для цикла while) пропускается.
    redo   -   начать новый цикл не вычисляя УСЛОВИЕ и не выполняя блок continue (для цикла while).

    Теперь более подробно о каждом из описанных ранее операторов цикла.

    Цикл while.   Общая форма:

            while (УСЛОВИЕ)
              {  БЛОК
              }
    или
            while (УСЛОВИЕ)
              {  БЛОК
              }
            continue
              {  БЛОК
              }
    
    В этом цикле БЛОК выполняет до тех пор, пока УСЛОВИЕ="истина".   Метка перед началом цикла нужна при использовании внутри блока цикла управляющих операторов next, last и redo.   Если метка все же отсутствует, то эти операторы ссылаются к началу ближайшего цикла.   Блок после continue выполняется всегда перед тем, как вычисляется логическое выражение УСЛОВИЕ.   Это подобно ДЕЙСТВИЕ в операторе for поэтому в этом блоке удобно изменять счетчики и флаги цикла даже если применяется оператор next.
        Пример 1:
            M1:
            while ($i<6)
              {  ++$i;            # Увеличиваем счетчик на 1
                 next M1 if $i<3; # Переходим в начало, если $i<3
                 ++$i;            # иначе увеличиваем счетчик еще на 1
              }
            continue
              {  print "$i ";     # Печатаем $i
              }
        Результат: 1 2 4 6
    
        Пример 2:
            M1:
            while ($i<6)
              {  ++$i;            # Увеличиваем счетчик на 1
                 last M1 if $i>3; # Выход из цикла, если $i>3
                 ++$i;            # иначе увеличиваем счетчик еще на 1
              }
            continue
              {  print "$i ";     # Печатаем $i
              }
        Результат: 2 4
    
        Пример 3:
            M1:
            while ($i<6)
              {  ++$i;             # Увеличиваем счетчик на 1
                 redo M1 if $i==3; # Далее пропустить для $i=3
                 ++$i;             # иначе увеличиваем счетчик еще на 1
              }
            continue
              {  print "$i "; # Печатаем $i
              }
        Результат: 2 5 7
    
    Цикл for.   Общая форма:
            for (ПРИСВОЕНИЕ; УСЛОВИЕ; ДЕЙСТВИЕ)
              {  БЛОК
              }
    
    Оператор for полностью аналогичен оператору for в Си.   Перед началом цикла выполняется ПРИСВОЕНИЕ, если УСЛОВИЕ="истина" выполняется БЛОК, затем выполняется ДЕЙСТВИЕ.
        Пример:
            for ($i=2; $i<5; ++$i)
              {  print $i, " ";
              }
            print "\nПосле цикла i=$i\n";
        Результат: 2 3 4
                   После цикла i=5
    
    Цикл foreach. Общая форма:
            foreach ПЕРЕМЕННАЯ (СПИСОК)
              {  БЛОК
              }
    
    В этом цикле ПЕРЕМЕННОЙ присваивается поочередно каждый элемент СПИСКА и выполняется БЛОК.   Если ПЕРЕМЕННАЯ опущена, то элементы присваиваются встроенной переменной $_.   Если в теле БЛОКА изменять значение ПЕРЕМЕННОЙ, то это вызовет изменение и элементов списка т. к. ПЕРЕМЕННАЯ фактически указывает на текущий элемент списка.

    Вместо слова foreach можно писать просто for - это слова синонимы.

        Пример 1:
            @month=("January", "February", "March"); # Создали массив
            foreach $i (@month)
              {  print $i, " ";                      # Печать $i
              }
        Результат: January February March
    
        Пример 2:
            @month=("January", "February", "March"); # Создали массив
            foreach $i (@month)
              {  $i=uc($i);                          # Изменили регистр
              }
            print @month;
        Результат: JANUARYFEBRUARYMARCH
    
        Пример 3:
            for $i (3, 5, 7)
              {  print "$i ";
              }
        Результат: 3 5 7
    
    На этом позвольте сегодня закончить наше очередное знакомство с языком Perl.




    CGI-программирование - Разбор POST и GET запросов


    Доступ к строке запросов

    Способом передачи данных CGI в скрипт является использование строки запросов.   Браузер передает данные HTTP-серверу как часть URL.   В свою очередь сервер рассматривает все, что следует за знаком вопроса (?) в URL, как строку запроса.

    Передать данные скрипту можно двумя методами - GET и POST.

    В случае использования формой при запросе метода GET принимающий запрос HTTP-сервер установит переменную окружения QUERY_STRING в виде name1=vaue1&name2=value2&...&nameN=valueN, при этом данные постоянно болтаются в строке адреса браузера: httр://www.domain.ru/cgi-bin/price.pl?Cat=POWER&Descr=varta
    В этом случае скрипт price.pl берет данные в переменной окружения QUERY-STRING: $data=$ENV{'QUERY_STRING'};

    При использовании метода POST данные передаются на стандартный вход скрипта.   Длина блока данных берется в переменной CONTENT_LENGTH: read(STDIN, $data, $ENV{'CONTENT_LENGTH'});

    В обоих случаях данные будут закодированы по следующему алгоритму: если ASCII код символа больше 32 и меньше 128, то он будет выдан без изменения.   Если символ - пробел, то он заменится на "+" (плюсик, без кавычек) все остальное преобразуется в вид %hh, где hh - шестнадцатеричный код символа.   То есть если Вы посылаете скрипту на сервер запрос вида:
    http://www.domain.ru/cgi-bin/price.pl?Cat=Видео&Descr=абвг,
    то при этом произойдет его перекодировка и в итоге получится:
    http://www.domain.ru/cgi-bin/price.pl?Cat=%C2%E8%E4%E5%EE&Descr=%E0%E1%E2%E3.


    Переменные CGI-среды, используемые при обработке запросов

    REQUEST_METHOD - Это одно из самых главных полей, используемое для определения метода запроса HTTP.   Протокол HTTP использует методы GET и POST для запроса к серверу.   Они отличаются тем, что при методе GET запрос является как бы частью URL, т.е. выглядит следующим образом: http://www.domain.ru/script.cgi?request а при методе POST данные передаются в теле HTTP-запроса (при методе GET тело запроса пусто).   И, следовательно, для CGI тоже есть различие: при методе GET запрос идет в переменную среды QUERY_STRING, а при методе POST подается на STDIN скрипта.
    Пример: REQUEST_METHOD=GET

    QUERY_STRING - Это строка запроса при методе GET.   Известно, что запрос из формы кодируется браузером поскольку не все символы разрешены в URL некоторые имеют специальное назначение.   Теперь о методе urlencode: неплохо бы чисто формально напомнить, что все пробелы заменяются в URL на знак '+', а все специальные и непечатные символы на последовательность %hh, где hh - шестнадцатеричный код символа, разделитель полей формы знак '&', так что при обработке форм надо произвести декодирование.
    Пример: QUERY_STRING=name=quake+doomer&age=20&hobby=games

    CONTENT_LENGTH - длина в байтах тела запроса.   При методе запроса POST необходимо считать со стандартного входа STDIN CONTENT_LENGTH байт, а потом производить их обработку.   Обычно методом POST пользуются для передачи форм, содержащих потенциально большие области ввода текста TEXTAREA.   При этом методе нет никаких ограничений, а при методе GET существуют ограничения на длину URL.
    Пример: CONTENT_LENGTH=31


    Декодирование HTML-форм с использованием метода GET

    Для получения от пользователя больше чем одного значения необходимо использовать формы.   Следующий скрипт генерирует форму.   Используя метод GET, скрипт дает команду браузеру послать значения запроса как часть URL.   Браузер может соединить несколько величин полей в одну строку запросов, разделяя поля с помощью амперсанда (&).  Для того чтобы скрипт мог определять значения полей, браузер включает имена полей в строку запроса.   Например, если база данных содержит три поля (имя, возраст и день рождения) с такими значениями (Mary, 27, 1.11.73), то строка запросов будет содержать значения полей в следующем формате:   name=Mary&age=27&birthday=1.11.73.

    В следующем примере скрипт декодирует поля и выводит на экран их значения с помощью создания HTML-формы:

    #!/usr/bin/perl
    
    $query=$ENV{QUERY_STRING};
    if ($query eq '')
      {  # сгенерируем форму
         print <<FORM;
    Content-type: text/html
    
    <HTML>
    <HEAD>
      <TITLE>Sample GET Form</TITLE>
    </HEAD>
    
    <BODY>
      What is your query?
      <P>
      <FORM METHOD="GET" ACTION="http://www.domain.ru/cgi-bin/get.cgi">
        A checkBox.<BR>
        <INPUT TYPE="checkbox" NAME="check" checked>
        <P>
        A radio button set.<BR>
        <INPUT TYPE="radio" NAME="button" VALUE="1"> 1<BR>
        <INPUT TYPE="radio" NAME="button" VALUE="2"> 2<BR>
        <INPUT TYPE="radio" NAME="button" VALUE="3"> 3
        <P>
        A data entry field<BR>
        <INPUT NAME="field">
        <P>
        Send the data.<BR>
        <INPUT TYPE="submit">
      </FORM>
    </BODY>
    </HTML>
    FORM
      }
    else
      {  # распечатаем результаты
         print "Content-type: text/html\n\n";
         print "<HTML>\n";
         print "<HEAD><TITLE>GET Form Result</TITLE></HEAD>\n";
         print "<BODY>\n";
         print "Your query values:<P>\n";
         @fields=split('&', $query);
         foreach (@fields)
           {  Switch:
                {  /^check=(.*)/ && do
                                      {  $check=$1;
                                         last Switch;
                                      };
                   /^button=(.*)/ && do
                                       { $button=$1;
                                         last Switch;
                                       };
                   /^field=(.*)/ && do
                                      {  $field=&decode($1);
                                         last Switch;
                                      };
                }
           }
         print "Check Box: $check<BR>\n";
         print "Radio Button: $button<BR>\n";
         print "Data Field: $field<BR>\n";
         print "</BODY>\n";
         print "</HTML>\n";
      }
    
    sub decode
    {  local ($value)=@_;
       $value=~s/\+/ /g;
       $value=~s/%([a-fA-F0-9][a-fA-F0-9])/pack('C', hex($1))/eg;
       return $value;
    }
            Листинг 1.
    
    В этом примере один и тот же скрипт создает форму и обрабатывает ее вывод, что достаточно нетрудно выполнить.   Такой способ рекомендуется для обработки форм, потому что концентрирует всю обработку в одном месте.

    Скрипт определяет, создавать ли форму или обработать запрос, в зависимости оттого, поступил ли запрос от пользователя.   Для обработки строки запросов скрипт разделяет запрос на поля, используя функцию split.   Далее скрипт сравнивает поля запроса с ожидаемыми именами полей.   Рассмотрим следующий пример:

    forech (@fields)
    {  Switch:
         {  /^check=(.*)/ && do
                               {  $check=$1;
                                  last Switch;
                               };
                 .
                 .
                 .
         }
    }
    
    Обычная форма цикла foreach включает в себя еще переменную $VAR, в которую записывается очередное значение из массива.   Если цикл foreach опускает эту переменную, то Perl использует переменную по умолчанию $_.   Аналогично, оператор регулярного выражения обычно выглядит следующим образом $VAR=~/PATTERN/.   Если переменная в выражении опущена, то Perl использует $_ как переменную по умолчанию, в результате чего цикл и регулярное выражение соответствуют друг другу.   Однако если слишком полагаться на переменные по умолчанию, то код на Perl'е может получиться неясным.   В данном же случае использование переменных, определенных по умолчанию, делает код более коротким и лучше читаемым.

    Далее обратите внимание на регулярное выражение, имеющее форму /^field=(.*)/.   Данное выражение указывает на необходимость начать поиск от начала строки, что предотвращает совпадения в середине имени другого поля.   Иными словами, имя поля и знак равенства (=) должны соответствовать сами себе.   Остающаяся часть регулярного выражения соответствует значению поля и извлекает его в переменную $1.   Поскольку $1 представляет собой временную переменную, то скрипт копирует ее в переменную с именем для каждого поля.   Скрипт использует подпрограмму decode для декодирования символов из полей, которые были закодированы браузером.


    Декодирование HTML-форм с помощью метода POST

    Следующий скрипт очень напоминает предыдущий, за исключением того, что данный скрипт использует для приема запроса метод POST.   Метод POST дает директиву браузеру послать данные формы, используя стандартный вход скрипта, а не строку запросов.   Полезность метода POST заключается в том, что он может обрабатывать большие объемы данных, тогда как метод GET ограничен пространством переменной сервера, а также длиной URL браузера.

    Для чтения данных со стандартного ввода скрипт может вызвать функцию read с нужным числом байт.   Размер строки запроса в байтах содержится в переменной окружения CONTENT_LENGTH.   После того как скрипт прочел строку запроса, дальнейшая обработка данных оказывается очень похожей на рассмотренную в предыдущем примере.   Для того чтобы сделать, этот скрипт более коротким, обработка строки запросов осуществляется с помощью несколько более сложного подхода:

    #!/usr/bin/perl
    
    $content_length=$ENV{CONTENT_LENGTH};
    read(STDIN, $query, $ENV{CONTENT_LENGTH});
    if (!defined($query) || $query eq '')
      {  # сгенерируем форму
         print <<FORM;
    Content-type: text/html
    
    <HTML>
    <HEAD>
      <TITLE>Sample POST Form</TITLE>
    </HEAD>
    
    <BODY>
      What is your query?
      <P>
      <FORM METHOD="POST" ACTION="http://www.domain.ru/cgi-bin/post.cgi">
        A checkBox.<BR>
        <INPUT TYPE="checkbox" NAME="cheсk" checked>
        <P>
        A radio button set.<BR>
        <INPUT TYPE="radio" NAME="button" VALUE="1"> 1<BR>
        <INPUT TYPE="radio" NAME="button" VALUE="2"> 2<BR>
        <INPUT TYPE="radio" NAME="button" VALUE="3"> 3
        <P>
        A data entry field<BR>
        <INPUT NAME="field">
        <P>
        Send the data.<BR>
        <INPUT TYPE="submit">
      </FORM>
    </BODY>
    </HTML>
    FORM
      }
    else
      {  # распечатаем результаты
         print "Content-type: text/html\n\n";
         print "<HTML>\n";
         print "<HEAD><TITLE>POST Form Result</TITLE></HEAD>\n";
         print "<BODY>\n";
         print "Your query values:<P>\n";
         @fields=split(/&/, $query);
         foreach (@fields)
           {  /(^.*)=(.*)/ && do
                               {  local ($field, $value)=($1, $2);
                                  $query{$field}=&decode($value);
                               }
           }
         print "Check Box: $query{cheсk}<BR>\n";
         print "Radio Button: $query{button}<BR>\n";
         print "Data Field: $query{field}<BR>\n";
         print "</BODY>\n";
         print "</HTML>\n";
      }
    
    sub decode
    {  local ($value)=@_;
       $value=~s/\+/ /g;
       $value=~s/%([a-fA-F0-9][a-fA-F0-9])/pack('C', hex($1))/eg;
       return $value;
    }
            Листинг 2.
    
    Этот скрипт использует одно регулярное выражение для анализа всех значений полей в строке запроса:
    /(^.*)=(.*)/ && do
                      {  local ($field, $value)=($1, $2);
                         $query{$field}=&decode($value);
                      }
    
    Вместо того чтобы присваивать значения каждого запроса отдельной переменной данная программа хранит все запросы в ассоциативном массиве.   В свою очередь скрипт может индексировать массив, используя нужные имена полей.


    Использование библиотеки cgi-lib для декодирования форм

    Общедоступная библиотека Perl cgi-lib.pl упрощает обработку CGI-форм.   Она содержит несколько полезных подпрограмм, однако наибольший интерес представляет подпрограмма ReadParse, которая читает и анализирует данные формы.   Одним из больших достоинств cgi-lib является то, что она прозрачно обрабатывает любой тип запросов форм (GET, POST) и даже формы, состоящие из многих частей для ввода больших объемов данных.

    Некоторые подпрограммы, такие как PrintHeader, HtmlTop, HtmlBot создают стандартные HTML-последовательности, но они слишком просты и не так полезны, как хотелось бы.   Для использования cgi-lib Вы включаете исходные коды с помощью директивы require.   Вы можете инсталлировать cgi-lib.pl туда же, где установлена стандартные библиотеки Perl'а, или вы можете ссылаться на нее, используя указание полного пути.   Для получения дополнительной информации или для загрузки библиотеки cgi-lib.pl посетите Web-узел http://www.bio.cam.ac.uk/cgi-lib.


    Некоторая модификация кода

    Я предполагаю, что приводимые выше конструкции разбора строки запроса не совсем очевидны, даже модифицированный вариант:
    @fields=split('&', $query);
    forech (@fields)
      {  /(^.*)=(.*)/ && do
                           {  local ($filed, $value)=($1, $2);
                              $query{$field}=&decode($value);
                           }
      }
    
    и поэтому предлагаю тот вариант, которым пользуюсь обычно сам:
    @pairs=split(/&/, $query);
    foreach $pair (@pairs)
      {  ($name, $value)=split(/=/, $pair);
         $value=~tr/+/ /;
         $value=~s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/ge;
         $FORM{$name}=$value;
      }
    
    В этом варианте мы еще раз используем Perl-функцию split, но теперь уже для того, чтобы разделить внутри одиночной записи поля имени и значения.   Кроме того, в этом варианте убирается знак "+" (точнее заменяется пробелом), который используется в строке запроса, когда одному полю имени соответствует несколько значений:   hobby=games&best=quake+doom+heretic&age=20

    Ну а теперь полный вариант этой подпрограммы.   Ее отличие от тех, которые описывались в примерах обработки GET и POST запросов в том, что она может работать с запросами обоих типов.   Для этого мы проверяем значение переменной CGI-среды REQUEST_METHOD, в которой указан метод пришедшего запроса и в зависимости от того какой это запрос читаем данные либо из переменной QUERY_STRING, либо читаем CONTENT_LENGTH байтов из стандартного потока ввода (STDIN).

    =head ############################################################
    
        Функция GetFormInput
    
        Производит разборку строки запроса для разделения названий
        полей запроса и их значений.  Функция работает с обоими
        методами запроса GET и POST.
    
        Функция возвращает:
            0 - Если метод запроса не опознан и сообщение об ошибке
            1 - Если разбор полей запроса был выполнен корректно и
                сообщение об успехе
    
    =cut #############################################################
    
    sub GetFormInput
    {  local($name, $value, $buffer, $err, $pair, @pairs);
    
       $err="Разбор запроса завершен.";
       if ($ENV{'REQUEST_METHOD'} eq 'GET')
         {  @pairs=split(/&/, $ENV{'QUERY_STRING'});
         }
       elsif ($ENV{'REQUEST_METHOD'} eq 'POST')
         {  read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
            @pairs=split(/&/, $buffer);
         }
       else
         {  $err="Метод запроса не опознан.  Используйте GET или POST.";
            return(0, $err);
         }
       foreach $pair (@pairs)
         {  ($name, $value)=split(/=/, $pair);
            $value=~tr/+/ /;
            $value=~s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/ge;
            $FORM{$name}=$value;
         }
       return(1, $err);
    }
            Листинг 3.
    
    И теперь еще один вариант подпрограммы разбора строки запроса, которая также как и предыдущая работает с обоими методами запроса GET и POST, но в отличие от нее допускает использование русских символов не только в качестве полей значений, но и в качестве полей имен.   Кроме того, в ней, в качестве примера, показано как вырезать из полей имен и значений HTML-комментарии (символы, ограниченные последовательностями <!-- и -->) и HTML-тэги (символы, находящиеся между < и >).   А в принципе Вы можете вырезать из текста запроса все, что душе угодно.
    =head ############################################################
    
        Функция GetFormInput
    
        Производит разборку строки запроса для разделения названий
        полей запроса и их значений.  Функция работает с обоими
        методами запроса GET и POST.
    
        Функция возвращает:
            Хэш, содержащий поля имен и значений запроса
    
    =cut #############################################################
    
    sub GetFormInput
    {  local (%userdata, $buffer, $name, $value, $pair, @pairs);
    
       if ($ENV{'REQUEST_METHOD'} eq 'GET')
         {  $buffer=$ENV{'QUERY_STRING'};
         }
       elsif ($ENV{'REQUEST_METHOD'} eq 'POST')
         {  read (STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
         }
       $buffer=~s/\+/ /g;
       @pairs=split(/&/,$buffer);
       foreach $ pair (@pairs)
         {  ($name, $value)=split(/=/, $ pair);
            $value=~tr/+/ /;
            $value=~s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
            $value=~s/<!--(.|\n)*-->//g;
            $value=~s/<([^>]|\n)*>//g;
            $name=~tr/+/ /;
            $name=~s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
            $name=~s/<!--(.|\n)*-->//g;
            $name=~s/<([^>]|\n)*>//g;
            if (defined($userdata{$name}))
              {  $userdata{$name}.=" : ".$value;
              }
            else
              {  $userdata{$name}=$value;
              }
         }
       return %userdata;
    }
            Листинг 4.
    
    Ну и напоследок рассмотрим примеры использования этих подпрограмм разбора запроса.
    #!/usr/bin/perl
    
    %FORM=();                              # определили и очистили хэш
    
    ($result, $mess)=&GetFormInput;        # произвели разбор запроса
    if ($result==0)                        # если неудачно - сообщение
      {  print "Content-type: text/html\n\n";
         print "<HTML>\n";
         print "<HEAD><TITLE>Error Page</TITLE></HEAD>\n";
         print "<BODY>\n";
         print "<B>$mess</B>\n";
         print "</BODY>\n";
         print "</HTML>\n";
         exit 0;
      }
    
                    # вывод полей имен и значений
    print "Content-type: text/html\n\n";
    print "<HTML>\n";
    print "<HEAD><TITLE>Result Page</TITLE></HEAD>\n";
    print "<BODY>\n";
    
    foreach $key (sort keys %FORM)
      {  print "$key=$FORM{$key}<br>\n"
      }
    
    print "</BODY>\n";
    print "</HTML>\n";
    
    
    sub GetFormInput
    {  local($name, $value, $buffer, $err, $pair, @pairs);
    
       $err="Разбор запроса завершен.";
       if ($ENV{'REQUEST_METHOD'} eq 'GET')
         {  @pairs=split(/&/, $ENV{'QUERY_STRING'});
         }
       elsif ($ENV{'REQUEST_METHOD'} eq 'POST')
         {  read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
            @pairs=split(/&/, $buffer);
         }
       else
         {  $err="Метод запроса не опознан.  Используйте GET или POST.";
            return(0, $err);
         }
       foreach $pair (@pairs)
         {  ($name, $value)=split(/=/, $pair);
            $value=~tr/+/ /;
            $value=~s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/ge;
            $FORM{$name}=$value;
         }
       return(1, $err);
    }
            Листинг 5.
    
    В данном скрипте наибольший интерес представляют строки выводящие значения имен и полей хэша, содержащего разобранную строку запроса:
    foreach $key (sort keys %FORM)
      {  print "$key=$FORM{$key}<br>\n"
      }
    
    Как известно функция foreach присваивает рабочей переменной поочередно все значения из указанного списка.   Для наглядности можно переписать первую из этих трех строк следующим образом: foreach $key (sort (keys (%FORM))) и теперь все стало на свои места.

    Действительно, в этой строке сперва происходит выборка всех ключей из хэша %FORM (это осуществляет функция keys), которые возвращаются затем в виде массива и сразу же попадают на вход функции сортировки.   После того, как функция sort отрабатывает, она возвращает переданный ей массив, но отсортированный в алфавитном порядке, который теперь и попадает на вход функции foreach.

    При использовании подпрограммы разбора строки запроса из листинга 4 сама программа укорачивается, но теряется возможность отслеживания корректности запроса.

    #!/usr/bin/perl
    
    %FORM=&GetFormInput;    # определили и заполнили хэш
    
    print "Content-type: text/html\n\n";
    print "<HTML>\n";
    print "<HEAD><TITLE>Result Page</TITLE></HEAD>\n";
    print "<BODY>\n";
    
    foreach $key (sort keys %FORM)
      {  print "$key=$FORM{$key}<br>\n"
      }
    
    print "</BODY>\n";
    print "</HTML>\n";
    
    
    sub GetFormInput
    {  local (%userdata, $buffer, $name, $value, $pair, @pairs);
    
       if ($ENV{'REQUEST_METHOD'} eq 'GET')
         {  $buffer=$ENV{'QUERY_STRING'};
         }
       elsif ($ENV{'REQUEST_METHOD'} eq 'POST')
         {  read (STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
         }
       $buffer=~s/\+/ /g;
       @pairs=split(/&/,$buffer);
       foreach $ pair (@pairs)
         {  ($name, $value)=split(/=/, $ pair);
            $value=~tr/+/ /;
            $value=~s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
            $value=~s/<!--(.|\n)*-->//g;
            $value=~s/<([^>]|\n)*>//g;
            $name=~tr/+/ /;
            $name=~s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
            $name=~s/<!--(.|\n)*-->//g;
            $name=~s/<([^>]|\n)*>//g;
            if (defined($userdata{$name}))
              {  $userdata{$name}.=" : ".$value;
              }
            else
              {  $userdata{$name}=$value;
              }
         }
       return %userdata;
    }
            Листинг 6.
    
    На этом на сегодня мы закончим разбор запросов получаемых скриптом.   Надеюсь, что эта информация будет Вам полезна.




    "Лучшая ссылка"

    Сегодня в этом разделе я хочу сказать пару слов о компании занимающейся выпуском непосредственно самого языка Perl, точнее, конечно же, его интерпретаторов.

    Если с Perl'ом для UNIX-подобных систем все понятно (как правило, он входит в комплект поставки), то для системы Windows все обстоит гораздо сложнее.   На данный момент мне встретилась только одна фирма, которая выпускает интерпретатор Perl'а для Windows.   Это ActiveState (http://www.ActiveState.com/), которая выпускает ActivePerl.

    Я предлагаю Вашему вниманию небольшой фрагмент перевода документации к 5.6 версии Perl'а.


    Что такое ActivePerl?

    Пакет ActivePerl - наиболее значительное и захватывающее усовершенствование Perl платформы!   Это - долгожданное "слияние" популярных Perl портов.

    Новые особенности в ActivePerl:

    • Глобализация и Юникод;
    • Параллельные интерпретаторы;
    • Степень детализации предупреждений;
    • Новые регулярные конструкции;
    • Дополнительная документация и обучающие программы;
    • Атрибуты подпрограмм;
    • 64-разрядные платформы;
    • Большие файловые системы.
    Законченный Пакет ActivePerl содержит:
    • Двоичное ядро Perl-дистрибутива;
    • Полную online документацию;
    • Менеджер Пакетов Perl.
    Версия Windows также включает:
    • Perl для ISAPI - IIS дополнение, которое делает perl CGI быстрее;
    • PerlScript - создание ActiveX сценариев, подобно JavaScript или VBScript;
    • PerlEz - Внедренный Perl.
    Доступны выпуски ActivePerl 5.6.0.613 для следующих платформ:
    • Linux x86 для RedHat 6.0
    • Linux x86 для Debian 2.1
    • Solaris sparc для Solaris 2.6
    • Windows x86 для Windows NT и Windows 2000

    Установка ActivePerl

    ActivePerl может быть легко установлен в родной формат каждой платформы.

                          Linux (x86)

    RedHat 6.0

    RedHat-совместимый пакет находится в формате RPM.

    % rpm -i ActivePerl-5.6.0.613-1.i386.rpm

    Этот пакет был проверен только с RedHat 6.0, но как ожидается, будет также совместимым с другими дистрибутивами RedHat, включая RedHat 7.0.


    Debian 2.1

    Debian-совместимый пакет находится в формате dpkg.

    % Dpkg -i ActivePerl-5.6.0.613.deb

    Этот пакет был проверен только с Debian 2.1, но как ожидается, будет совместимым с другими дистрибутивами Debian 2.x.


                          Solaris (sparc)

    Пакет Solaris находится в формате pkgadd.

    % Gunzip ActivePerl-sol26-5.6.0.gz
    % pkgadd -d ActivePerl-sol26-5.6.0

    Этот пакет был проверен только с Solaris 2.6, но как ожидается, будет совместимым с другими дистрибутивами, включая Solaris 2.7.


                          Windows (x86)

    Пакет Windows находится в формате Microsoft Windows Installer.

    На Windows NT и Windows9x Вам, возможно, придется загрузить и установить пакет поддержек MSI, чтобы начать процесс установки.   Пользователи Windows 2000 не нуждаются в дополнительном программном обеспечении, чтобы установить пакет.

    Прежде, чем Вы можете устанавливать ActivePerl, пользователи Windows NT будут должны загрузить и установить Инсталлятор Windows 1.1 из:
    http://www.ActiveState.com/download/contrib/Microsoft/NT/InstMsi.exe
    http://www.ActiveState.com/download/contrib/Microsoft/9x/InstMsi.exe

    Мы рекомендуем выполнить инсталляцию с административными привилегиями.   Выполнение без административных привилегий имеет следующие ограничения:

    • Особенность PerlScript будет недоступна.
    • PerlMSG.dll не установлен.
    • Переменные среды будут установлены только для текущего пользователя.
    • Ассоциирование файла для Perl-файлов заблокировано.
    • Любые созданные записи системного реестра находятся в HKEY_CURRENT_USER а не в HKEY_LOCAL_MACHINE.

    Известные Проблемы

                          Linux и Solaris

    Программа Suidperl не включена в этот пакет из-за потенциальных проблем защиты.   Если Вы желаете использовать suidperl в вашей инсталляции, мы рекомендуем собрать Perl из источника.   Исходный текст для ActivePerl доступен в http://www.ActiveState.com/.


                          Windows

    PerlScript в настоящее время функционирует не правильно, когда работает в ASP среде под IIS 5.   Это, кажется, проблема в библиотеке MSVCRT.DLL, которая поставляется с Windows 2000.   Мы работаем, чтобы решить проблему для будущего выпуска.

    ActivePerl зависит от библиотеки MSVCRT.DLL установленной в системе.   Этот файл поставляется с Windows 98, Windows NT и Windows 2000, но не с Windows 95.   Если Вы не устанавливаете этот файл в вашей системе, Вы можете столкнуться с проблемами при установке и/или при выполнении скриптов в ActivePerl.

    Вы можете сами загрузить библиотеку MSVCRT.DLL из: ftp://ftp.microsoft.com/softlib/mslfiles/msvcrt.exe.




    Наши друзья

    Всем читателям данной рассылки я хочу порекомендовать для использования компьютерную библиотеку "InfoCity", которая располагается по адресу: http://www.infocity.kiev.ua/ и содержит огромное количество книг и статей по следующим направлениям:
    • C/C++, Perl, Java, Asm, Delphi, Pascal, Basic, Fortran
    • HTML, JScript/VBScript, Серверы, CGI, Разное
    • Win9x/NT, Unix, Linux, NetWare
    • Криптография, Debuging, Разное
    • FoxPro, Oracle, SQL, ERwin, Разное
    • Локальные сети
    • Программные руководства
    • Компьютерное железо
    • Графика
    • Разное
    Кроме того, всем читателям моей рассылки я рекомендую рассылку, ведомую координатором проекта "InfoCity" Михаилом Пинкусом (icity@mail.ru): "Новости компьютерной библиотеки InfoCity".

    В рассылке публикуются новые поступления книг и статей компьютерной тематики в библиотеке "InfoCity" (http://www.infocity.kiev.ua).   Все: от языков программирования, операционных систем и интернет технологий до баз данных, графики и программных руководств.

    Здесь Вы можете сразу и подписаться на эту рассылку.   Для этого просто вставьте Ваш e-mail в соответствующее поле, ну и конечно же не забудьте нажать на кнопочку "OK", и все. :-))

    Рассылки Subscribe.Ru
    Новости компьютерной библиотеки InfoCity

    Адрес рассылки "Новости компьютерной библиотеки InfoCity" в Каталоге subscribe.ru: http://subscribe.ru/catalog/comp.paper.infocity/

    Архив рассылки "Новости компьютерной библиотеки InfoCity" находится на subscribe.ru по адресу: http://subscribe.ru/archive/comp.paper.infocity/

    InfoCity - виртуальный город технической документации.




    О рассылке

    В связи с тем, что ко мне часто приходят письма вновь подписавшихся с просьбой выслать предыдущие номера рассылки или указать где их можно взять, я даю адреса на subscribe.ru связанные с моей рассылкой.

    Домашняя страница этой рассылки в Каталоге subscribe.ru: http://subscribe.ru/catalog/comp.soft.prog.perlprog/, здесь же Вы можете и подписаться на данную рассылку.

    Архив этой рассылки находится на subscribe.ru по адресу: http://subscribe.ru/archive/comp.soft.prog.perlprog/

    Данная рассылка распространяется только через subscribe.ru.

    Если Вам понравилась эта рассылка, и Вы считаете, что она может быть полезна и другим, то отправьте ее своим друзьям.




    Обращение к рекламодателям

    Уважаемые господа!   Если Вы хотите разместить свою рекламу на страницах этой рассылки, то обращайтесь со своими предложениями к ее автору, то есть ко мне, по адресу vitalij@newmail.ru и я сообщу Вам дополнительные сведения.




    На этом на сегодня и закончим.   До новых встреч.


    Виталий Ярошевский vitalij@newmail.ru


    При подготовке данной рассылки были использованы следующие материалы:

    1. "Основы программирования на языке Перл"     Маслов В. В. - "Радио и связь" 1999
    2. "Введение в Perl"     Андрей Новиков
    3. "Учебное пособие по CGI-программированию от Леши"     Леша


    P. S.   Дорогие мои подписчики, у меня есть к Вам ОГРОМНАЯ просьба: не надо присылать мне письма с предложениями участвовать в программе RMI.   Меня это не интересует.



    http://subscribe.ru/
    E-mail: ask@subscribe.ru

    В избранное