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

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


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

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


Здравствуйте дорогие мои подписчики.


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




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


Вот, наконец, и третье тысячелетие на пороге.   И в этот раз оно надеюсь, наступит, в отличие от прошлого года.

Я поздравляю всех Вас, дорогие мои подписчики, с наступающим Новым годом.   Хочу пожелать Вам в 2001 году добиться огромных успехов в жизни.

Сразу хочу предупредить, что с выходом следующего номера рассылки возможна задержка.   Ну, сами понимаете. :-)

"Еще одно знаменательное событие", обещанное в прошлом выпуске, свершиться не успело, но работа над ним идет полным ходом и в январе 2001 года обязательно завершится.

Сегодня у нас в разделе, посвященном CGI-программированию две коротенькие темы.   Но обо всем по порядку.   Сегодняшние темы:

  • Perl для начинающих - Преобразование кодировок
  • CGI-программирование - Счетчик посещений : Модификация
  • CGI-программирование - Гостевая книга : Безопасность




    Perl для начинающих - Преобразование кодировок


    Сегодня мы начнем практически применять накопленные знания, и начнем со скрипта преобразования кодировки.   На его примере мы рассмотрим работу с командной строкой, проверки условий, циклы, работу со строками и файлами и, кроме того, работу с подпрограммами.

    Итак, приступим.

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

    Самой первой строкой в Perl-программе должен быть путь к интерпретатору Perl'а.   Для Unix-систем это будет:

            #!/usr/bin/perl
    
    или
    
            #!/usr/local/bin/perl
    
    другие пути встречаются намного реже.   Для Windows/DOS-систем этот путь может быть что-то типа:
            #!c:/compile/perl/bin/perl.exe
    
    или любой другой, смотря где на Вашем компьютере установлен интерпретатор Perl'а.

    Условимся, что запуск программы будем выполнять с тремя параметрами:

    1. исходный файл, подлежащий перекодировке;
    2. целевой файл, куда будет записан перекодированный текст;
    3. режим работы - из какой кодировки в какую будет производиться перекодирование.

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

    Делается это при помощи следующего фрагмента кода:

    if (@ARGV<3)
      {  print "trnfile - file codetable translator.\n";
         print "(c)IVA 2000\n\n";
         print "Usage: trnfile file_in file_out mode\n";
         print "       mode - from_table2to_table\n\n";
         print "Used tables: dos, win, koi\n\n";
         print "Example: trnfile index.html index_win.html koi2win\n";
         exit;
      }
    
    Здесь @ARGV - массив, содержащий параметры строки запуска программы.   Проверка if (@ARGV<3) возвращает "истину" если число аргументов меньше трех и "ложь" в противном случае.   Если результат проверки - "ложь", т. е. количество аргументов, переданных программе, удовлетворяет требованиям, то переходим к выполнению программы.

    Теперь мы определяем три строки, содержащие DOS, Win и KOI кодировки русского алфавита (все 66 символов) - это переменные с именами $dos, $win и $koi, соответственно.   В принципе можно было бы представить кодировки не строками, а массивами, но в этом случае определения таблиц становится более громоздким.   Кроме того, мы ничего не выиграем при поиске символа не в переменной-строке, а в переменной-массиве.

    $dos=" ЎўЈ¤Ґс¦§Ё©Є«¬-®ЇабвгдежзийклмнопЂЃ‚ѓ„…р†‡€‰Љ‹ЊЌЋЏђ‘’“”∙–—™љ›њќћџ";
    $win="абвгдеёжзийклмнопрстуфхцчшщъыьэюяАБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ";
    $koi="БВЧЗДЕЈЦЪЙКЛМНОПРТУФХЖИГЮЫЭЯЩШЬАСбвчздеіцъйклмнопртуфхжигюыэящшьас";
    
    Заранее хочу предостеречь Вас от возможной ошибки.   Дело в том, что я верстаю рассылку в кодировке Win-1251, а на Subscribe ее автоматически перекодируют в формат KOI-8, в связи с чем могут произойти (а точнее именно произойдут) некоторые изменения в таблицах кодировок.   Для того чтобы восстановить их, Вам можно воспользоваться любым текстовым редактором с поддержкой этих кодировок.   В качестве такового могу предложить обычный F4-редактор из состава DOS Navigator'а.

    Теперь нам нужно проверить корректность задания режима работы. Программа допускает указание 6 режимов работы:

    • win2dos - из Win-кодировки в DOS-кодировку;
    • win2koi - из Win-кодировки в Koi-кодировку;
    • dos2win - из DOS-кодировки в Win-кодировку;
    • dos2koi - из Win-кодировки в Koi-кодировку;
    • koi2win - из Koi-кодировки в Win-кодировку;
    • koi2dos - из Koi-кодировки в DOS-кодировку.
    Проверка осуществляется следующим образом.   В переменной $mode находится строка с режимом, указанным пользователем.   Нужно проверить имеет ли место вхождение последовательностей dos, win или koi в эту строку.   Проверяется это при помощи функции работы со строками index(строка, подстрока), которая возвращает номер символа, с которого указанная подстрока начинается в строке.   Мы проверяем на вхождения с нулевой и четвертой позиции каждую из трех последовательностей.   Если найдено вхождение с нулевой позиции, то присваиваем строку с нужной кодировкой переменной $in_tab.   Если найдено вхождение с четвертой позиции, то присваиваем строку с нужной кодировкой переменной $out_tab.   Если вхождение последовательности не найдено, то увеличиваем на 1 переменную $notuse, которая указывает, что кодировка не используется.   Вот так это выглядит на примере проверки вхождения в строку режима последовательности "dos":
    if (index($mode, "dos")==0)
      {  $in_tab=$dos
      }
    elsif (index($mode, "dos")==4)
      {  $out_tab=$dos;
      }
    else
      {  $notuse++;
      }
    
    После того, как все три вхождения проверены, мы проверяем значение переменной $notuse.   Если ее значение больше 1, это значит, что режим трансляции был задан неверно.   Мы выводим сообщение об этом и завершаем программу:
    die "Error specified \"mode\" param." if ($notuse>1);
    
    Продолжим.   Теперь мы открываем оба файла.   Первый для - чтения (по умолчанию), а второй - для записи (явно):
    open F1, "$ARGV[0]" || die "Cannot open $ARGV[0] $!\n";
    open F2, ">$ARGV[1]" || die "Cannot open $ARGV[1] $!\n";
    
    В Perl'е начальный индекс массива, как и в С, равен нулю.   В этих выражениях, как Вы видите, используется оператор "логическое или" - "||" у которого правая часть не выполняется, если левая вернет значение "истина", т. е. если файл откроется. Если же файл не откроется, то выполнится правая часть, в которой находится оператор die, он выведет строку "Cannot open имя_файла код_ошибки" и завершит выполнение программы.

    Теперь, после того, как оба файла открыты и начинается самое интересное.

    Мы читаем по одной строке из первого файла, преобразуем кодировку и записываем строку во второй файл:

    while (<F1>)
      {  $newline=&trans($_);
         print F2 $newline;
      }
    
    По достижении конца первого файла цикл прекращается, и мы закрываем оба файла:
    
    close(F1);
    close(F2);
    
    Теперь непосредственно о преобразовании кодировки.

    Для преобразования мы берем прочитанную строку и вводим цикл, с количеством итераций равным длине строки:

    for ($i=0; $i<length($instr); $i++)
    
    Далее в этом цикле с помощью функции substr(строка, начало, длина) получаем поочередно каждый символ строки:
    $symb=substr($instr, $i, 1);
    
    После того, как символ получен, мы определяем позицию его вхождения в строку исходной кодировки:
    $pos=index($in_tab, $symb);
    
    Если такой символ в строке имеется (т. е. если это любая строчная или заглавная буква русского алфавита), то мы из строки целевой кодировки по полученной позиции считываем символ и добавляем его в конец создаваемой строки.   Если же такой символ в строке отсутствует, то добавляем его в конец создаваемой строки без изменений:
    if ($pos>=0)
      {  $newsymb=substr($out_tab, $pos, 1);
         $outstr.=$newsymb;
      }
    else
      {  $outstr.=$symb;
      }
    
    Вроде бы все.   Осталось сказать только, что сама процедура перекодировки реализована в виде подпрограммы с именем "trans".

    А теперь полный текст программы trnfile.pl:

    #!/usr/bin/perl
    
    if (@ARGV<3)
      {  print "trnfile - file codetable translator.\n";
         print "(c)IVA 2000\n\n";
         print "Usage: trnfile file_in file_out mode\n";
         print "       mode - from_table2to_table\n\n";
         print "Used tables: dos, win, koi\n\n";
         print "Example: trnfile index.html index_win.html koi2win\n";
         exit;
      }
    
    $dos=" ЎўЈ¤Ґс¦§Ё©Є«¬-®ЇабвгдежзийклмнопЂЃ‚ѓ„…р†‡€‰Љ‹ЊЌЋЏђ‘’“”∙–—™љ›њќћџ";
    $win="абвгдеёжзийклмнопрстуфхцчшщъыьэюяАБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ";
    $koi="БВЧЗДЕЈЦЪЙКЛМНОПРТУФХЖИГЮЫЭЯЩШЬАСбвчздеіцъйклмнопртуфхжигюыэящшьас";
    
    $notuse=0;
    $in_tab="";
    $out_tab="";
    
    $mode=$ARGV[2];
    
    if (index($mode, "dos")==0)
      {  $in_tab=$dos
      }
    elsif (index($mode, "dos")==4)
      {  $out_tab=$dos;
      }
    else
      {  $notuse++;
      }
    
    if (index($mode, "win")==0)
      {  $in_tab=$win
      }
    elsif (index($mode, "win")==4)
      {  $out_tab=$win;
      }
    else
      {  $notuse++;
      }
    
    if (index($mode, "koi")==0)
      {  $in_tab=$koi
      }
    elsif (index($mode, "koi")==4)
      {  $out_tab=$koi;
      }
    else
      {  $notuse++;
      }
    
    die "Error specified \"mode\" param." if ($notuse>1);
    
    open F1, "$ARGV[0]" || die "Cannot open $ARGV[0] $!\n";
    open F2, ">$ARGV[1]" || die "Cannot open $ARGV[1] $!\n";
    while (<F1>)
      {  $newline=&trans($_);
         print F2 $newline;
      }
    close(F1);
    close(F2);
    
    sub trans
    {  $instr= $_[0];
       $outstr="";
    
       for ($i=0; $i<length($instr); $i++)
         {  $symb=substr($instr, $i, 1);
            $pos=index($in_tab, $symb);
            if ($pos>=0)
              {  $newsymb=substr($out_tab, $pos, 1);
                 $outstr.=$newsymb;
              }
            else
              {  $outstr.=$symb;
              }
         }
       return ($outstr);
    }
    
    На этом на сегодня все.




    CGI-программирование - Счетчик посещений : Модификация


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

    В процессе его эксплуатации мною, была выявлена одна странность: при вызове скрипта с параметром числа цифр меньше чем 7 скрипт формирует нормальный рисунок, в противном случае изображение формируется некорректно.   В принципе казалось бы 6 цифр - это ведь почти миллион посещений, ну кто наберет столько?   Но почему бы и нет.   Почему бы не 5, 6, 10 миллионов, конечно со временем.   И что тогда?   И вот поэтому я предлагаю модификацию счетчика.   Она весьма проста.   Нужно просто фрагмент кода:

    if (($digits_in_image)>scalar(@count))
      {  for ($i=0; $i<($digits_in_image-scalar(@count)); $i++)
           {  unshift @count, 0;
           }
      }
    
    заменить на:
    
    if (($digits_in_image-1)>length($counter))
      {  for ($i=0; $i<($digits_in_image-1-length($counter)); $i++)
           {  unshift @count, 0;
           }
      }
    
    Теоретически, в данном примере, выражения length($counter) и scalar(@count) должны возвращать одинаковые значения, т. к. в первом случае вычисляется длина переменной $counter (которая, как Вы помните, в Perl'е является строкой), равная количеству цифр, а во втором - количество элементов массива @count, равное, опять-таки количеству цифр, но этого почему-то не происходит.

    После этой замены скрипт формирует корректно изображение с любым количеством цифр.




    CGI-программирование - Гостевая книга : Безопасность


    После выхода прошлого номера рассылки ко мне пришло письмо, которое я хочу частично процитировать.   Надеюсь, автор не будет против.

    Здравствуйте, Виталий!
    Поздравляю Вас с наступающим праздником!
    Желаю Вам и Вашей рассылке всего самого наилучшего в новом году (и новом веке)!

    Пробовал Вашу Гостевую книгу из 9-го выпуска.   Очень понравилась.
    Дополнительная функция, которую, по моему мнению, надо бы в нее внести - защита от тэгов в сообщениях.
    Гости-то ведь бывают разные :-)
    В гостевых книгах некоторых сайтов (очевидно, имеющих такую защиту), я встречал "дружеские сообщения" типа

    1. #include virtual="/usr/bin/cat /etc/passwd"
           (очевидно, guestbook "съел" угловые скобки :-)) )
    2.  script window.location="http://мой_хороший_сайт/"; /script
    
    и подобные.

    Можно сделать и др. "неподобающие вещи" - например, вставить код своего баннера и т.п.

    ...

    С уважением,
    Андрей
    andrew@bars.agava.ru
    http://angel07.webservis.ru/

    Я совершенно согласен с Андреем и предлагаю дополнение к гостевой книге.   Для этого нужно немного модифицировать код процедуры разбора входного запроса.   Заменять будем следующий блок:

    foreach $pair (@pairs)
      {  ($name, $value)=split(/=/, $pair);
         $value=~tr/+/ /;
         $value=~s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/gem;
         $FORM{$name}=$value;
      }
    
    Можно пойти по пути удаления всего, что заключено между символами угловых скобок, т. е. вырезать все HTML-комментарии, в которых могут быть заключены SSI-инструкции, и все HTML-тэги.   Сделать это можно следующим образом:
    foreach $pair (@pairs)
      {  ($name, $value)=split(/=/, $pair);
          $value=~tr/+/ /;
          $value=~s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/gem;
          $value=~s/<!--(.|\n)*-->//gem;  # вырезка многострочных HTML-комментариев
          $value=~s/<([^>]|\n)*>//gem;    # вырезка HTML-тэгов
          $FORM{$name}=$value;
      }
    
    А можно ничего не удалять.   Просто взять и заменить все угловые скобки, на их коды.   Выглядеть это будет просто замечательно.   На экране вместо вставленного в сообщение баннера будет его HTML-код.   Делается это так:
    foreach $pair (@pairs)
      {  ($name, $value)=split(/=/, $pair);
          $value=~tr/+/ /;
          $value=~s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/gem;
          $value=~s/</&lt;/gem;
          $value=~s/>/&gt;/gem;
          $FORM{$name}=$value;
      }
    
    Удачных Вам гостей.




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


    Сегодня в этом разделе речь пойдет о компьютерной библиотеке RTFM - VinNest Documentation Home (http://rtfm.vn.ua/).

    Ссылка на этот ресурс мне попалась давно, но мой поход туда по ряду причин постоянно откладывался.   И как оказалось совершенно зря.   Причина проста.   Дело в том, что на этом сайте находится, процитирую: "Почти полное собрание книг O'Reilly по программированию на Perl, Java, WWW программированию, TCP/IP и устройству UNIX систем.   28 книг в онлайн."   И ведь действительно все это там есть.

    По программированию на Perl'е там находятся следующие книги:

    • Perl in a Nutchell
    • Learning Perl
    • Learning Perl on Win32 Systems
    • Programming Perl
    • Advance Perl Programming
    • Perl Cookbook
    правда есть одно мааааааааааааленькое неудобство.   Все книги на английском языке, но для тех, кто с ним знаком, я думаю, это не проблема.

    Для тех, кого заинтересовала эта информация, даю ссылку прямо на книги: http://rtfm.vn.ua/prog/perl/orb/books/perl/index.html




    О рассылке


    В связи с тем, что ко мне часто приходят письма вновь подписавшихся с просьбой выслать предыдущие номера рассылки или указать где их можно взять, я даю адреса на 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


    P. S.   Еще раз поздравляю всех с наступающим Новым Годом.

    http://subscribe.ru/
    E-mail: ask@subscribe.ru
    Поиск

    В избранное