Ряд соображений по поводу Perl
(Записки продвинутого чайника)

    В данной статье речь пойдёт о таком языке как Perl. Обычно он упоминается в связи с созданием гостевых книг, форумов, чатов и прочих приложений CGI. На самом деле возможности этого языка гораздо шире и одним CGI не ограничиваются. Изначально Perl использовался в операционной системе UNIX, для которой он, собственно и был изобретён как язык создания отчётов и обработки текстов. С ростом популярности этого языка были созданы версии и для Macintosh, и для MS-DOS/Windows. Автору сих тэгов довелось иметь дело с Perl'ом на Виндовском компьютере, используя этот язык именно по своему первоначальному назначению, т.е. никаких CGI или опросных форм (я использовал различные версии среды ActivePerl фирмы Active State1). Perl сразу же увлёк и даже очаровал, несмотря на трудную для восприятия приверженность к многочисленным невербальным комбинациям метасимволов. Среди преимуществ этого языка, бросившихся в глаза при первом знакомстве, следует упомянуть следующие особенности: переменные, массивы, хэши и т.д. создаются "на лету", переменные, операторы и функции могут быть интерпретированы внутри символьной строки (имеются в виду такие конструкции как print "My name is $name", где программа выведет вместо имени переменной $name её значение). Также отсутствует жёсткая типизация: одна и та же переменная может использоваться и как строковая, и как количественная, и как имя файла, не надо отвлекаться на перевод из одного типа данных в другой. Для тех целей, с которыми я использовал Perl, да и вообще для языка обработки текстовой информации, это очень важная особенность. Я не профессионал в программировании, но хотя так или иначе имею с ним дело уже много лет, до сих пор не понял привлекательности строгой типизации для создателей компьютерных языков. Не зря кто-то сказал, что Perl сочетает в себе простоту BASIC'а и мощь C - не в последнюю очередь благодаря вышеперечисленным особенностям и сохранению процедурного синтаксиса наравне с возможностью создания классов и объектов. Далее, в Perl'е встроена изощрённейшая и ухищрённейшая система поиска, замены, перестановок и бог весть чего ещё, что делает его просто незаменимым для операций над текстом и строками (лингвисты должны оценить это как никто другой). К примеру, для перевода письменного текста в фонетическую или фонологическую транскрипцию нужно лишь немногое:

  1. Открыть файл и записать его содержимое в строковую переменную2; (это удобно, однако если размер файла превышает 70-80 КБ, то тогда работа программы заметно замедляется. К сожалению, операторы замены не работают с файлами.)
  2. создать два массива - X, который содержит буквы, и Y (фонетические символы);
  3. запустить цикл и применить оператор замены - s/X/Y/g, при этом не забыв параметр g, который позволяет проделать данную операцию во всём тексте;
  4. закрыть файл.

А вот как выглядит код (в качестве примера мы заменим буквы исландского алфавита á, ó, ú, é на их фонологическую транскрипцию /aU/, /oU/, /u/, /je/):

open (FILE, "isl.txt");
until (eof (FILE))
{
  $mystring .= <FILE>;
}
close (FILE);

@let_array = ('á','ó','ú','é');
@fonem_array = ('aU','oU','u','je');

for ($i = 0; $i <= $#let_array; $i++) {
  $mystring =~ s/$let_array[$i]/$fonem_array[$i]/g;
}

open (FILE,">isl_out.txt");
print FILE $mystring;
close (FILE);

# Открытие файла

# Запись содержимого в переменную
# $mystring







# Операция замены


# Запись результата в новый файл

    Вот и все дела. Наличие встроенных многофункциональных операторов подстановки и замены m/.../, s/.../.../, tr/.../.../ (=y/.../.../) отличает Perl в выгодную сторону от многих других языков, где вам пришлось бы создавать самому соответствующие процедуры или классы. Если вы лингвист, то, конечно же, отметите, что вышеописанный пример не всегда будет работать правильно, в частности, если нам попадаются дифтонги и диграфы, но эти трудности имеют только технический характер. К тому же, компетентный человек должен догадываться, что для абсолютно точного перевода текста в фонетическую запись одних программных средств будет недостаточно - даже почти что всемогущий Perl не умеет определять морфологические границы, что фонологу, увы, приходится постоянно учитывать и поправлять ручками...
    Ещё один пример, теперь уже из совершенно другой области - создания веб-страниц. Допустим, у вас есть много html-страниц, в которых задан белый цвет фона, а вам, захотелось его поменять на зелёный! Я для этого раньше открывал такие страницы в WordPad'е и запускал текстовую замену: '<body bgcolor="#FFFFFF" ' заменить на '<body bgcolor="#008000" ' Времени это занимало не так уж много, но и его было жалко, к тому же иногда я по недосмотру пропускал файлы. А потом я догадался написать следующий сценарий на Perl'е, который это делал за какие-то секунды. Предваряя листинг кода этой программы сделаем следующую оговорку: допустим, наши файлы имеют однообразные имена: chapter1.htm, chapter2.htm и всего их у нас 10 штук:

$max = 10;
for ($i=1;$i<=$max;$i++) {

  $htm = "chapter$i.htm";
  open (FILE, $htm);
  until (eof (FILE))
    {
    $mystring .= <FILE>;
    }
  close (FILE);

  $mystring =~ s/bgcolor=\"#FFFFFF\"/bgcolor=\"#008000\"/g;

  open (FILE,">$htm");
  print FILE $mystring;
  close (FILE);
  <>;
  $mystring='';
}













# Операция замены





# Не забудьте обнулить
# переменную!

    А вот ещё такой пример. Многим известна такая неприятная особенность последних версий Microsoft Word запоминать html-файлы не в формате HTML, а в XML (скажите спасибо, что не в VRML :-) ). В результате получается страница неуёмно большого объёма (а зачастую и папка с дополнительными файлами), что нежелательно именно для веб-программирования, которое всегда стремится к максимальной компактности - быстрота закачки это экономия денег клиента (изредка мне попадаются в сети страницы, созданные именно в Word'е и качаются они действительно слишком медленно). Но с этим можно справиться, преобразовав страницу из XML в HTML. Наша стратегия будет заключаться в следующем: очистить всю голову страницы (т.е. пространство между тэгами <head> и </head>), а также параметры большинства тэгов, потом стереть тэги <div>, <span>, <style>, <font> и эксклюзивные тэги XML <xml>, <o:>, <!>. Конечно, мы потеряем часть форматирования и ссылки, но выигрыш окажется более весомым, или, точнее, менее весомым - в 2 раза, а то и больше:

open (FILE, "myxml.htm");
until (eof (FILE))
{
  $mystring .= <FILE>;
}
close (FILE);

$mystring =~ s/<p(.*?)>/<p>/gs;
$mystring =~ s/<i(.*?)>/<i>/gs;
$mystring =~ s/<a(.*?)>/<a>/gs;
$mystring =~ s/<b(.*?)>/<b>/gs;
$mystring =~ s/<body(.*?)>/<body>/gs;
$mystring =~ s/<div(.*?)>/<div>/gs;
$mystring =~ s/<span(.*?)>/<span>/gs;
$mystring =~ s/<font(.*?)>/<font>/gs;
$mystring =~ s/<!(.*?)>/<!>/gs;
$mystring =~ s/<html(.*?)>/<html>/gs;
$mystring =~ s/<o:(.*?)>/<o:>/gs;
$mystring =~ s/<\/o:(.*?)>/<o:>/gs;
$mystring =~ s/<w:(.*?)>/<o:>/gs;
$mystring =~ s/<\/w:(.*?)>/<o:>/gs;
$mystring =~ s/<meta(.*?)>/<o:>/gs;
$mystring =~ s/<xml(.*?)>/<xml>/gs;
$mystring =~ s/<style(.*?)>/<style>/gs;
$mystring =~ s/<head>(.*?)<\/head>/<head><\/head>/gs;
$mystring =~ s[</p>.*?<p>][<br>&nbsp;&nbsp;&nbsp;&nbsp;]gs;

@tagArr = ('<div>','</div>','<span>','</span>','<font>',
'</font>','<!>','</!>','<o:>','<xml>','</xml>','<style>','</style>');
$taglen = $#tagArr;
for ($k = 0;$k <=$taglen;$k++) {
  cutTag($tagArr[$k]);
}




open (FILE,">myhtml.htm");
print FILE $mystring;
close (FILE);

sub cutTag {
$c = length $mystring;
$in = shift;
$l = length $in;
for ($i = 0;$i <= $c;$i++){
  $sub = substr ($mystring,$i,$l);
  $j = substr($mystring,$i,1);
  if ($sub eq $in) {
    $i += $l-1;
    next;}
  $new = $new.$j;
  }
$mystring = $new;
$new = '';
}









# "Зачистка" тэгов.
# Параметр /s
# позволяет наряду с
# обычными
# символами вычищать
# ещё и символы
# новой строки. 













# Стирание лишних тэгов.
# В их разряд попал и тэг
# <font> - это связано с
# тем, что Word, также как
# и Frontpage, порой 
# грешит излишней
# "щедростью" на этот
# тэг.






# Подпрограмма,
# очищающая параметры
# тэгов.
#










 Один полезный совет: если вы пишите страницу на русском, то не забудьте вставить следующую строку в голову страницы после завершения её "очищения":

<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">

Впрочем, в любом случае вас ожидает некоторая ручная доработка страницы.
    Обращает на себя внимание не только простота, с которой Perl помогает нам решить подобные задачи, но и то, что в Windows 98 отсутствуют встроенные инструменты для совершения подобных операций. Другое дело - семейство UNIX, где используется несколько языков-оболочек (shell), предоставляющих широкий набор функций, необходимых для работы с операционной системой - кстати, многие их особенности унаследовал и сам Perl. Почему такие мощные средства отсутствуют в продуктах фирмы Microsoft для меня остаётся большой загадкой.3 Есть лишь MS-DOS 7.10, встроенный в Windows 98, но его shell-язык, имеется в виду командный язык bat-файлов, настолько примитивен, что в нём даже отсутствуют такие элементарнейшие вещи, как функции ввода и математические операторы, в результате чего для выполнения, к примеру, умножения двух одноразрядных чисел приходится применять поистине героическую изворотливость (см., напр., здесь). Вот тут-то и выясняется, насколько полезным здесь может оказаться Perl. При этом обратим внимание, что предыдущие примеры в Unix могли быть вполне выполнены имеющимися возможностями оболочки (на которой даже можно писать CGI-приложения). Теперь такая возможность появляется и у Windows благодаря способности интерпретатора Perl обрабатывать инструкции в bat-файлах при его запуске с параметром -x. Это значит, что вызвав в bat-файле программу Perl.exe -x, вы можете передать ей команды, заключённые в этом же bat-файле. Есть программа pl2bat.bat (папка Perl\bin), которая преобразует файлы Perl с расширением .pl в bat-файлы. При этом к тексту исходной программы добавляются следующие строки:

@echo off
if "%OS%" == "Windows_NT" goto WinNT
perl -x -S "%0" %1 %2 %3 %4 %5 %6 %7 %8 %9
goto endofperl
:WinNT
perl -x -S %0 %*
if NOT "%COMSPEC%" == "%SystemRoot%\system32\cmd.exe" goto endofperl
if %errorlevel% == 9009 echo You do not have Perl in your PATH.
if errorlevel 1 goto script_failed_so_exit_with_non_zero_val 2>nul
goto endofperl
#!perl

#здесь будет находиться текст исходного файла с расширением .pl

__END__
:endofperl

     Заметим, что поскольку мы наверняка знаем, какая у нас стоит операционная система (а именно Win98), и если мы не претендуем писать скрипты для нескольких ОС, то всю эту конструкцию можно упростить, выкинув оттуда упоминания о WinNT. В результате остаётся следующее:

@echo off
perl -x -S "%0" %1 %2 %3 %4 %5 %6 %7 %8 %9
#!perl

#здесь будет находиться текст исходного файла с расширением .pl

__END__
:endofperl

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

@echo off
copy templ.bat %1.bat
edit %1.bat

    Теперь мы запишем досовский каркас будущего Perl-сценария в файл templ.bat и введём следующую команду: doscript myfile, в результате чего появится файл myfile.bat, где уже будет вписана необходимая начальная информация. Конечно, вся эта морока с bat-файлами необходима только тогда, когда вы намеренно вышли из графической оболочки Windows и оперируете в MS-DOS'е. В самой Windows достаточно будет создать обычный файл Perl и его запустить. Однако раз уж мы захотели усовершенствовать именно программный язык оболочки этой операционной системы, то продолжим играться с bat-файлами, где можно совместить возможности и Perl, и MS-DOS.
    Отметим, что Perl подходит для использования его в качестве shell-языка именно потому, что его не надо отдельно компилировать, т.е. преобразовывать в исполняемый exe-файл, состоящий из машинного кода. Написал себе скрипт и поручил программе-интерпретатору Perl.exe его выполнять, будь то файл с расширением .pl или .bat - и всё, вы тут же получаете доступ к богатым возможностям Perl и его огромной библиотеке.
    Можно попробовать составить специальную библиотеку таких сценариев. Не имеет смысла переопределять уже имеющиеся DOS-команды, тем более что их можно вызывать из самого Perl'а при помощи функций exec,system и `...`. А вот дополнить имеющиеся возможности DOS'а Perl может легко. Мне как-то понадобилось провести перепись всех файлов папки \Windows с учётом времени их последнего изменения (была одна програмка, которая после её удаления и тщательнейшей очистки реестра всё равно оставляла какие-то следы своего пребывания на диске, что давало о себе знать при её повторной установке). В этом случае мне помогла программа listfil.bat следующего содержания4:

@echo off
perl -x -S "%0" %1 %2 %3 %4 %5 %6 %7 %8 %9
goto endofperl

#!perl

use Time::gmtime; 
use File::stat;

open FILE, ">report.log";
$startseek = 'e:\windows';  
seekinfo($startseek);
close (FILE);

######## Начало блока подпрограмм ########

sub seekinfo {


opendir (DIREC, $_[0]);
 

chdir($_[0]);
@arr = readdir(DIREC); 
closedir (DIREC); 
print FILE "$_[0]\n"; 

for ($i=0;$i<=$#arr;$i++) {

  $dir = -d $arr[$i]; 


  if ($dir) {
    if ($arr[$i] ne '.' && $arr[$i] ne '..') 
      {push (@dirarr, $arr[$i]);} 
    }

  else {
     push (@filarr, $arr[$i]); 
     }

  }
  @dirarr = sort @dirarr; 
  @filarr = sort @filarr;

  dirlist (@dirarr); 
  filelist (@filarr);

  $startseek = shift(@subarr); 
  while ($startseek ne '') {
    seekinfo($startseek); 
   }

}

sub dirlist { 

$c = $#dirarr;
for ($i=0;$i<=$c;$i++) { 
  $dirname = shift(@dirarr);
  push (@subarr, "$startseek\\$dirname");
  $dirname = timecheck($dirname);

  print FILE "d $dirname";
  }
}

sub filelist { 

$c = $#filarr;
for ($i=0;$i<=$c;$i++) {
  $filename = shift(@filarr);
  $filename = timecheck($filename);
  print FILE "- $filename";
  }
}

sub timecheck { 

$datec = gmctime(stat("$_[0]")->ctime);
$datem = gmctime(stat("$_[0]")->mtime);
$time = "$_[0]\t$datec\t$datem\n"; 
return $time;
}
__END__
:endofperl







# Подключение модулей
# форматирования вывода
#  времени и свойств файла

# Корневая директория поиска




# Подпрограмма поиска имён файлов и # папок

# открытие директории (первый элемент # массива аргументов @_)



# перепись её содержимого в массив
# закрытие директории
# распечатка её полного пути



# $dir = "истина" если $arr[$i] -
# директория


# не учитывать псевдопапки "." и ".."
# занести в конец массива имён папок



# иначе - в массив имён файлов



# сортировка обоих массивов по
# алфавиту

# распечатать имена папок и файлов в
# данной директории



# Рекурсивный вызов процедуры
# поиска для субдиректорий



# Процедура перечисления папок





# определение времени создания и
# изменения5




# Процедура перечисления файлов









# Функция, определяющая время
# создания/изменения файлов/папок






    Теперь наберём команду listfil, которая в течение каких-то секунд сгенерирует файл report.log (размером порядка 400 КБ), содержащий опись всех директорий и файлов в папке \Windows со временем их создания и изменения. Фрагмент этого файла мы вам продемонстрируем:

c:\Windows
d APPLOG                  Sat Mar 3 18:19:28 2001       Sat Mar 3 18:19:30 2001
d All Users                   Sat Mar 3 18:23:15 2001       Sat Mar 3 18:23:16 2001
d Application Data     Sat Mar 3 18:16:12 2001       Sat Mar 3 18:16:12 2001
d CATROOT               Sat Mar 3 18:15:56 2001       Sat Mar 3 18:15:56 2001
d COMMAND             Sat Mar 3 18:13:42 2001       Sat Mar 3 18:13:42 2001
d CONFIG                   Sat Mar 3 18:15:24 2001       Sat Mar 3 18:15:24 2001
d CURSORS               Sat Mar 3 18:14:14 2001       Sat Mar 3 18:14:14 2001
d Cookies                    Sat Mar 3 18:24:49 2001       Sat Mar 3 18:24:50 2001
d DRWATSON            Sat Mar 3 18:15:22 2001       Sat Mar 3 18:15:22 2001
.......................
- 1STBOOT.BMP          Wed May 5 21:22:00 1999         Wed May 5 21:22:00 1999
- ARJ.PIF                       Sun Mar 4 12:00:09 2001           Tue Jul 27 03:01:00 1999
- ARP.EXE                    Sat Mar 3 19:01:39 2001           Wed May 5 21:22:00 1999
- ASD.EXE                    Wed May 5 21:22:00 1999         Wed May 5 21:22:00 1999
- ASPI2HLP.SYS          Wed May 5 21:22:00 1999         Wed May 5 21:22:00 1999
.......................
c:\Windows\All Users\Application Data
c:\Windows\All Users\DRM

- DRMv1.bak                 Mon Apr 16 21:44:59 2001         Mon Apr 16 21:36:04 2001
- DRMv1.key                 Mon Apr 16 21:36:03 2001         Mon Apr 16 21:36:04 2001
- drmv2.lic                      Mon Apr 16 21:36:26 2001         Mon Apr 16 21:36:28 2001
- drmv2.sst                     Mon Apr 16 21:36:26 2001         Mon Apr 16 21:36:54 2001
- v2ks.bla                       Mon Apr 16 21:36:26 2001         Mon Apr 16 21:36:28 2001
- v2ks.sec                      Mon Apr 16 21:36:26 2001         Mon Apr 16 21:36:28 2001
c:\Windows\All Users\Главное меню
d Программы               Sat Mar 3 18:27:05 2001            Sat Mar 3 18:27:06 2001
c:\Windows\All Users\Рабочий стол
c:\Windows\Application Data\Identities

d {097E9820-4C13-11D7-BB00-B2F6479DEA66} Sat Mar 3 18:24:29 2001  Sat Mar 3 18:24:30 2001
c:\Windows\Application Data\Microsoft
d AddIns                          Sun Mar 4 18:48:06 2001           Sun Mar 4 18:48:08 2001
d Address Book             Sun Mar 25 14:54:09 2001         Sun Mar 25 14:54:10 2001
.......................

    И это всего лишь один скромный пример возможностей применения Perl'а в качестве языка оболочки операционной системы. К сожалению, в одном bat-файле можно использовать не более одного сценария Perl'а (иначе программа сбивается на бесконечный цикл, причины чего я ещё не совсем понял), однако ничто не мешает вам продолжать писать команды DOS после метки :endofperl. Как бы то ни было, уже полученные результаты должны вселять оптимизм в отношении возможности использования Perl для создания приложений под Windows.


1 Последний дистрибутив Active Perl вы можете бесплатно загрузить на сайте этой компании.
2 или просто скажем "в переменную", см. выше о типизации.
3 Существуют, правда, оболочки 4Dos и 4NT фирмы JPSoftware - не очень известные программы, я их не пробовал, но слышал достаточно скептические отзывы. Можно ещё поставить Cygwin - целый эмулятор Linux'а под Windows. К слову сказать, его полная установка занимает на моём компьютере 2 ГБ (с Apache, Х11, документацией, исходниками и т.д.) - для сравнения, объём папки \windows составляет у меня всего лишь 540 МБ. Однако ничто не мешает вам инсталлировать только основной пакет программ Сюгвина, по умолчанию указанный в установочной программе - он должен занимать гораздо меньше места. Как бы то ни было, благодаря Сюгвину вы получите доступ к большинству юникс-функций, при том что по умолчанию они будут действовать только в той папке, куда установлен сей диковинный зверь. Его разработчики не рекомендуют ставить его в корневую директорию диска из-за опасения совпадения названий папок у него с какой-нибудь другой программой. Я, правда, ещё ни разу не видел виндовско-досовские программы, что создавали бы в корневом разделе папки usr, var, sbin, etc и т.д.... Но на всякий случай я при установке последовал совету в целом глубоко уважаемых мною авторов, что, впрочем, не смертельно, т.к. к остальному дисковому пространству можно обращаться через виртуальную директорию /cygdrive/drive_letter. Эта забавная "ОС" оказалась хороша не только для непосредственного обращения к юниксовским программам, но и как учебный тренажёр команд и оболочек UNIX/Linux - очень рекомендую!!!
4 По каким-то причинам в Dos 7.10 отсутствует программа tree, которая, в частности, есть в последней самостоятельной версии ОС MS-DOS 6.22, - она осуществляет перечисление всех папок в указанной директории. Её отсутствие приводит к ощутимому удлинению нашей программы.
5 Точнее, функции ctime и mtime выдают, соответственно, время последнего изменения индексного дескриптора файла и время последнего изменения файла.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 



Hosted by uCoz