| Содержание | Предисловие | В |
Глава 1В этой главе:
Назначение языка Perl Доступность Основные понятия Прогулка по стране Perl Упражнение ВведениеИстория создания языка PerlСлово Perl является аббревиатурой выражения Practical Extraction and Report LanguageПозабавившись немного с этой версией, добавив кое-что, Ларри предложил ее сообществу читателей материалов телеконференций Usenet,В результате Perl все рос и рос, причем почти с той же скоростью, что и операционная система UNIX. (СпециальноПовысилась переносимость. То, что когда-то было компактным языком, теперь сопровождается сотнями страниц документации, в состав которой входят десятки man-страниц, Примеры программ, содержащихся в этой книге, мы проверяли на Perl версии 5.004 (на момент написания книги этоНазначение языка PerlНазначение языка Perl — помочь программисту в выполнении рутинных задач,Научившись пользоваться языком Perl, вы, возможно, обнаружите, что начинаете тратить меньше времени на правильное заключение в кавычки различных параметровКак и любой язык, Perl может быть языком "только_для_Доступность Если при попытке вызвать Perl из sneil вы получите сообщениеэто значит, что вашего системного администратора еще не охватила Perl-лихорадка. Если язык Perl не инсталлирован в вашей системе,Perl распространяется по открытой лицензии GNU (GNU Public License)*, согласно которой "вы можете распространяПо сути дела, Perl не только бесплатен, но и работает достаточно хорошо почти на всем, что называет себяПрограммисты настолько увлеклись языком Perl, что, помимо UNIX- и UNIX-подобных систем, начали использовать егИли по несколько более либеральной "художественной лицензии" (Artistic License), согласно которой Perl<Основные понятия Сценарий shell — это не что иное, как последовательность команд shell,[результат выполнения команды date, затем команды who] %Аналогичным образом Perl-программа — это набор Perl-операторов и определений, записанных в виде файла. Затем включается бит исполнения *,В большинстве случаев операция введения такого признака заключается в помещении в начало файла строки #!/usr/bin/perl Если же ваш Perl инсталлирован в каком-то нестандартном каталоге или если ваша система "Perl — это, в основном, язык со свободным форматом записи программ (вроде С) — пробельные символы, включаемые* Это справедливо для UNIX-систем. Указания о том, как сделать сценарий исполняемым в других системах, вы можете найти в сборнике ответов на часто задаваемые вопросколичество пробельных символов между лексемами во всех прочих случаях могут быть произвольными. Хотя почти каждую Perl-программу можно записать в одну строку, эти программы обычно пишут с отступами, как С-программы, причем вложенные операторы записывают с большим отступом, чем охватывающие. В этой книге вы увидите множество пример Аналогично сценарию shell, Perl-программа состоит из всех операторов Perl, имеющихся в файле и рассматриваемыхКомментарии в Perl похожи на комментарии shell (современные). Комментарием является все, что следует за незаклВ отличие от большинства shell (но аналогично awk и sed),Но процедура компиляции все же требует времени, и применение большой Perl-программы, которая быстро выполняет одну маленькую задачу (из множества тех, которые она способна выполнить),Perl, таким образом, работает и как компилятор, и как интерпретатор. С одной стороны, это компилятор, потому что перед выполнением первого оператора программы она полностью Прогулка по стране PerlНаше путешествие по стране Perl мы начнем с небольшой прогулки. В ходе этой прогулки мы ознакомимся с некоторыми возможносПрограмма Hello, WorldДавайте рассмотрим небольшую программу, которая делает что-то реальное. Вот ваша базовая программа, которая выводит на экран слова "Hello, World":^! /usr/bin/perl -w print ("Hello, World\n") ; Первая строка говорит о том, что это программа написана на языке Perl. Кроме того, первая строка является комментарием; ведь коммеВторая строка — это выполняемая часть данной программы. Здесь мы видим функцию print.Когда вы вызываете эту программу, ядро запускает интерпретатор Perl, который разбирает всю программу (обе строки, включая первую, т.е. комментарий), а затем выполняет компил* Точку с запятой можно опустить, если оператор является последним оператором в блоке или файле либо оператором eval.Скоро вы увидите Perl-программы, в которых print и другие функции иногда вызываются с круглыми скобками, а иногдаКак задавать вопросы и запоминать результат Давайте немного усложним пример, ведь приветствие Hello, World — слишком простое и статичное. Сделаем так, чтобы программаОдно из мест для хранения значений (вроде имени) — скалярная переменная. Для хранения вашего имени в нашей программе мы используем скалярную переменнуюПрограмма должна иметь возможность спросить у вас имя. Для этого нам нужен способ выдачи приглашения и способ принятия от вас данных. Предыдущая программа показала нам, как можно приглашать, используя для этого функциюprint "What is your name? "; # Как ваше имя? $name = <STDIN>;Значение переменной $name пока содержит завершающий символ новой строки (имя RandaiТеперь нам нужно лишь сказать Hello и указать значение переменной $name,orint "Hello, $name ! \n"; Как и в shell, если нам нужен именно знак доллара, а не ссылка на скалярную переменную, мы можем предварить этот знак обратной косСложив все вместе, получаем: •ft! /usr/bin/perl -w print "What is your name? " ; $name = <STDIN>; chomp ($name) ; print "Hello, Sname ' \n" ; Добавляем возможность выбора Допустим теперь , что у нас припасено какое-то особое приветствие для пользователя по имени Рэндал, а для остальных<#!/usr/bin/perl print "What is your name? "; $name = <STDIN>; chomp ($name} ; if ($name eq "Randal") { print "Hello, Randal! How good of you to be here!\n"; } else { print "Hello, $name! \n"; # обычное приветствиеВ операции eq сравниваются две строки. Если они равны (т.е. совпадают все символы и длина строк одинакова), результатом будет "истОператор if выбирает, какой блок операторов (заключенных между парными фигурными скобками) выполняется; если выражение даетСекретное слово Теперь, когда у нас есть имя, давайте сделаем так, чтобы человек, выполняющий эту программу, угадывал секретное слово. У всех, кроме Рэндала, программа будет непрерывно требовать ввести #! /usr/bin/perl -w $secretword = "llama"; # секретное слово print "What is your name? "; $name = <STDIN>; chomp $name;if ($name eq "Randal") { print "Hello, Randal! How good of you to be here!\n"; * } else { print "Hello, $name ' \n"; # обычное приветствие print "What is the secret word? "; $guess = <STDIN>; chomp ($guess) ; while ($guess ne $secretword) {print "Wrong, try again. What is the secret worcl.•' '• ; $guess = <STDIN>; chomp ($guess) ; Сначала мы задаем секретное слово, помещая его в скалярную переменную $secretword. После приветствия программа спрашивает (посредсКонечно, эта программа плохо защищена, потому что любой, кому надоест угадывать секретное слово , может просто прервать ее выполнение и вернуться к приглашению, а то и подсмоНесколько секретных слов Давайте посмотрим, как можно модифицировать эту программу так, чтобы она принимала несколько секретных слов. Используя то, что мы уже видели, можно было бы многократно сравнивать вариант-до Более эффективное решение — хранить все возможные ответы в структуре данных, которая называется список,@words = ("camel", "llarna", "alpaca"); Имена переменных-массивов начинаются с символа @ , что позволяет отличать их от имен скалярных переменных. Существует еще один спос@words = qw (camel llama alpaca) ; Это абсолютно то же самое; операция qw работает так, как будто мы взяли в кавычки каждую из трех строк.Присвоив значения элементам массива, мы можем обращаться к каждому из них по индексной ссылке. Так, $words [0] — это camel, $words [1] — llama, a $words#! /usr/bin/perl -w @words == qw (camel llama alpaca); print "What is your name? " ; $name = <STDIN>; chomp ($name) ; if ($name eq "Randal") { print "Hello, Randal! How good of you to be here!\n"; } else {print "Hello, $name ! \n"; # $i = 0; # сначала попробуем это слово $correct = "maybe"; # догадка верна или нет? while ($correct eq "maybe") {if ($words [$i] eq $guess) { # верно?$correct = "yes"; # да! } elsif ($i < 2) { # смотреть еще слова?$i==$i+l; # в следующий раз посмотреть следующее слово } else { # больше слов нет, должно быть, неверно$i = 0; # вновь начать проверку с первого слова } } # конец цикла while для неверных слов } # конец цикла "не РэнЗаметьте, что мы используем скалярную переменную $correct для того, чтобы показать, все еще ищем мы правильный пароль или уже нашли его.В этой программе показан также блок eisif оператора if-then-eise. Такой конструкции нет ни в одном другом языкРазные секретные слова для разных пользователей В предыдущем случае любой пользователь мог угадать одно из трех секретных слов и получить доступ к программе. Если мы хотим, чтобы для каждого пользователя было задано свое секретное сл
Обратите внимание: у последних двух пользователей одинаковые секретные слова. Такое допускается. Самый простой способ сохранить такую таблицу — использовать хеш. В каждом элементе хеша содержится отдеSwords = qw( fred camel barney llama betty alpaca wilma alpaca ); Каждая пара в этом списке представляет в хеше один ключ и соответствующее ему значение. Обратите внимание на то, что мы разбили эту процедуру присваивания на несколько строк без каких-либо симв Чтобы найти секретное слово для Betty, мы должны использовать имя Betty как ключ в ссылке на хешСведя все это воедино , мы получаем такую программу:#! /usr/bin/perl -w %words = qw ( fred camel barney llama betty alpaca wilma alpaca) ; print "What is your name? "; $name = <STDIN>; chomp ( $name) ; if ($name eq "Randa l") {print "Hello, Randal! How good of you to be here!\n"; } else { print "Hello, $name !\n"; # обычное приветствие $secretword = $words {$name};} } Обратите внимание на то , как происходит поиск секретного слова. Если имя не найдено, остальная часть программы удалена . . . ] $secretword = $words ($name}; # получить секретное словоif ( $secretword eq "") { # не найдено$secretword = "groucho"; # конечно,# можно использовать} print "What is the secret word? "; [. .. остальная часть программы удалена .. . ]Обработка различных вариантов ввода секретного елова Если вместо Randai вы введете Randai L. Schwartz или randa* На самом деле это значение undef, но для операции eqДопустим, вы хотите найти все строки, которые начинаются со слова Randal, а не просто строку, равнуюif ($name =~ /^Randal/) ( +f да, совпадает ) else ( ## нет, не совпадаетОбратите внимание на то, что регулярное выражение выделяется косой чертой с обеих сторон. Пробелы и другие пробельные символы, заключенные между косыми, имеют значение, поскольку они являются ч Это почти решает нашу задачу, но не позволяет выбрать randal или отклонить Randall. Чтобы принять#! /usr/bin/perl %words = qw ( fred camel barney llama betty alpaca wilma alpaca ); print "What is your name? "; $name = <STDIN>; chomp ($name); if ($name =~ / ^randal\b/i) {print "Hello, Randal! How good of you to be here!\n"; } else { print "Hello, $name! \n"; # $secretword = "groucho"; t конечно, можно использовать }print "What is the secret word? "; $guess = <STDIN>; chomp ($guess , while ($guess ne Ssecretword) ( print "Wrong, try again. What is the secret word? "; $guess = <STDIN> ; chomp ($guess) ; Как видите, эта программа уже довольно далека от простенькой Hello, World.В Perl имеется все, что необходимо для работы с регулярными выражениями, т.е. он предоставляет все возможности, которые обеспечиваСправедливость для всех Итак, теперь я могу ввести Randal, randal или Randal L. Schwartz,Чтобы быть справедливыми по отношению к Барни, мы должны перед поиском имени в таблице взять первое слово из того, что введСначала — операция подстановки: мы хотим взять содержимое переменной $ name, найти первый специальный (не испо$name =~ s/\W.*//; Мы используем ту же операцию =~, что и раньше, но справа у нас теперь стоит операция подстановки —Однако GNU-версия утилиты egrep выполняет эту операцию гораздпримере — это пустая строка между второй и третьей косыми.) Эта операция выглядит и выполняется во многом так же, как оперТеперь для того, чтобы перевести все оставшиеся символы в нижний регистр, мы преобразуем эту строку с помощью операции tr*. Она очень похожа на UNIX-команду$name =~ •tr/A-Z/a-z/; Между косыми заключены списки искомых и заменяющих их символов. Дефис между буквами а и z обозначает все символы, находящиеся межд#!/usr/bin/perl Swords " qw ( fred camel bamey llama betty alpaca wilma alpaca print "What is your name? "; $name = <STDIN>; chomp ($name); $original name = $name; # сохранить для приветствия $name =~ s/\W.*//; # избавиться от всех символов, следующих после первого слова$name =~ tr/A-Z/a-z/; # перевести все в нижний регистр if ($name eq "randal") ( * теперь можно так сравнитьprint "Hello, Randal! How good of you to be here!\n"; else ( print "Hello, $original_name! \n"; ^ $secretword == "groucho"; 4 конечно, можно использовать }print "What is the secret word? "; $guess = <STDIN>; chomp ($guess); while ($guess ne $secretword) ( * Символы с диакритическими знаками такому переводу не поддаются. Подробности см. на man-странице рег11оса** Специалисты заметят ,что мы также могли написать нечто вродез/(\3*) .*/\L$1/, чтобы сделать все это за один присест, но специалprint "Wrong, try again. What is the secret word? "; $guess = <STDIN>; chomp ($guess); Обратите внимание на то, что сопоставление с регулярным выражением для слова Randai вновь выполняется с помощью обычной операции сИтак, благодаря всего нескольким операторам наша программа стала гораздо более дружелюбной. Вы увидите, что проведение сложных манипуляций со строками посредством всего лишь нескольких нажатий Отметим, однако, что в процессе обработки имени (т.е. при его модификации, необходимой для проведения операции сравнения и поиска соответствия в таблице) первоначально введенное имя уничтожаетс В Perl имеется много способов, позволяющих проводить анализ и изменение символов в строках. С большинством из них вы познакомитесьПовышение степени модульности Теперь, когда мы добавили так много строк к нашему первоначальному коду, нам при его просмотре будет непросто уловить общую логику построения программы. Поэтому было бы неплохо отделить В Perl существует понятие подпрограммы, имеющей параметры и возвращаемые значения. Подпрограмма определяется в программе один раз, но использоваться можДавайте создадим для нашей маленькой, но быстро растущей программы подпрограмму good_word, которая будет принимать имя и вариант слова и возвращать значение "истина", если эsub good _word (my($somename,$someguess) = @_; # назвать параметры $somename =~ s/\W.*//; # избавит# первого слова$somename =~ tr/A-2/a-z/; t перевести все символы в нижний регистр if ($somename eq "randal") ( # не нужно угадыватьreturn 1; # возвращаемое значение — true I elsif (($words($somename} 11 "groucho") eq $someguess) (return 1; # возвращаемое значение — true ) else { return 0; * возвращаемое значение — falseВо-первых, определение подпрограммы состоит из зарезервированного для этих целей слова sub, за которым идет имя подпрограммы и блоПервая строка в данном конкретном определении — это операция присваивания, с помощью которой значения двух параметров подпрограммы копируются в две локальные переменные с имСледующие две строки удаляют символы, стоящие после имени (точно так же, как в предыдущей версии программы). Оператор if-elsif-else позволяет определить, является ли введенный пользователем вариант слова ($someguess) веДля того чтобы подпрограмма немедленно возвращала в вызвавшую ее программу указанное в подпрограмме значение, можно воспользоваться оператором возврата. В отсутствие явного оператора возвращаемым значением является последнее выражение, Проверка в части eisif выглядит довольно сложной; давайте разобьем ее на фрагменты:($words($somename) И "groucho") eq $someguessПервый элемент в круглых скобках обеспечивает проведение уже знакомого нам хеш-поиска, в результате которого отыскивается некоторое значение в массиве %wordsзначит, что ключ $ some name находился в хеше), то оно и будет являться значением данного выражения. Если ключ найден не был,В любом случае, будь то значение из хеша или принимаемое по умолчанию значение groucho, мы сравниваем его с вариантом, вводимым пользователем. Если результат сравнения положВыразим все это в виде правила: если имя — randal или если вводимый пользователем вариант соответствует одному из имен, находящемуся в массиве %words#! /usr/bin/perl Swords = qw( fred camel barney llama betty alpaca wilma alpaca ) ; print "What is your name? "; $name = <STDIN>; chomp ($name) ; if ($name "~ /^randal\b/i) ( t обратно на другой путь :-)print "Hello, Randal! How good of you to be here!\n"; I elscj/ { print "Hello, $name! \n"; t while (! good word( $name, $guess)) ( print "Wrong, try again. What is the secret word? "; ?guess “ <STDIN>; chomp ($guess) ; t... здесь вставляется определение подпрограммы good_word() .,,]Обратите внимание: мы вновь вернулись к использованию регулярного выражения для проверки наличия имени Randal в массиве, потому чтНаибольшее отличие этой программы от предыдущей состоит в том, что здесь используется цикл while, содержащий подпрограмму &good_word.в данном случае $name. Аналогичным образом $someguess передЗначение, возвращаемое этой подпрограммой (1 или 0, если вы помнитеОбратите внимание: при разработке этой подпрограммы предполагалось, "то значение хеша %words задается в основнСтоль деликатный подход к использованию глобальных переменных вызван тем, что обращаться с ними нужно очень аккуратно. Говоря в общем, переменные, не созданные с помощью ту, глобальны для всей программы, тогда как переменные ту действую Перенос списка секретных слов в отдельный файл Допустим, вы хотели бы использовать список секретных слов в трех программах. Если вы сохраните этот список так, как мы уже это делали, нам придется корректировать все три программы (если, н Поэтому давайте поместим список слов в файл, а затем, чтобы ввести список в программу, просто прочитаем файл. Для этого нужно создать канал ввода-вывода, который называется дескриптором файла. Ваша Perl-программа автоматически по Это делается с помощью следующего кода: sub init words ( open ('..'ORDSLIST, "wordslist"); while ($name = <WORDSLIST” ( chomp ($name); $word = <WORDSLIST>; chomp ($word); $words ($name} = Sword; close (WORDSLIST) ; Мы помещаем его в подпрограмму, чтобы не загромождать основную программу. Это означает также, что позже мы сможем изменить место хранения списка слов и даже его формат. Произвольно выбранный формат списка слов — один элемент в строке с чередованием имен и секретных слов. Для нашей базы данных мы имели бы такой список:fred camel barney llama betty alpaca wilma alpaca Функция open инициализирует дескриптор файла wordslist, связывая егПри выполнении цикла while читаются строки из файла wordslist (через дескриптор файла wordЕсли бы вы выполняли программу с ключом -w, вам пришлось бы проверять, определено ли полученное возвращаемое значение. Пустая строка, которую возвращает операцияwhile ( defined ($name = <WORDSLIST) ) ( * Но если бы вы были еще более осторожны, вы, вероятно, проверили бы также и то, возвращает ли функция open значение "истинаС другой стороны, при нормальном развитии событий мы считываем строку (включая символ новой строки) в переменную $ name. Сначала с помощью функции chomp<Последняя строка цикла while помещает $word в %wordsПо завершении чтения файла его дескриптор можно использовать повторно, предварительно закрыв файл с помощью функции close. (Дескрипторы файлов автоматически закрываются в люСделанное выше определение подпрограммы может идти после другого аналогичного определения или перед ним. Вместо того чтобы помещать определение %words в начало программы, мыfr! /usr/bin/perl init words () ; print "What is your name? "; $name = <STDIN>; ;homp $name; if ($name =~ /^randal\b/i) ( * обратно на другой путь :-)print "Hello, Randal! How good of you to be here!\n"; I else ( print "Hello, $name! \n"; # ^hile (! good word($name, $guess)) I print "Wrong, try again. What is the secret word? "; $guess = <STDIN>; :homp ($guess); } ## далее — подпрограммыs ub init_words ( open (WORDSLIST, "wordslist") 11die "can' tl open woraJ.isT:: ^' .ihile ( defined ($name = <WORDSLIST”) ( chomp ($name) ; $word = <WORDSLIST>; chomp $word; $words( $name) = $word; I close (WORDSLIST) II die "couldn't close wordlist: $"' iub good word ( my($somename,$someguess) = @_; * перечислить параметры $somename =~ s/\W.*//; # удалить все символы, стоящие после первого слова'somename =~ tr/A-Z/a-z/; # перевести все символы в нижний регистр :.f ($somename eq "randal") ) <t не нужноreturn 1; * возвращаемое значение - true elsif (($words($somename) II "groucho") eq $someguess) (return 1; * возвращаемое значение - true else ( •eturn 0; i возвращаемое значение — fТеперь написанный нами код начинает выглядеть как настоящая "взрослая" программа. Обратите внимание: первая выполняемая строка — вФункция open используется также для открытия файлов при выводе в них информации и открытия программ как файлов (здесь она лишь упомянута). Полное описание этой функции будетКак обеспечить скромный уровень безопасности ' Этот список секретных слов должен меняться минимум раз в неделю!", — требует Главный Директор Списковлучше всего делать это в подпрограмме init_words ( ) ; мы уже раоотаем в ней с файлом wordlist. Perl-опsub init words ( open (HORDSLIST, "wordslist") 11 die "can't open wordlist: $!"; if (- М WORDSLIST >= 7.0) ( f в соответствии с бюрократическими правиламиdie "Sorry, the wordslist is older than seven days. "; I while ($name = <WORDSLIST” ( chomp ($name) ; $word = <WORDSLIST> ; chomp ($word) ; $words($name} = $word; close (WORDSLIST) 11 die "couldn't close wordlist: $!"; Значение -м wordslist сравнивается со значением 7. Если оно больше,Остальная часть программы изменений не претерпевает, поэтому в целях экономии бумаги мы ее повторять не будем. Помимо определения "возраста" файла мы можем узнать имя его владельца, размер, время последнего доступа к нему и все остальные сведения, хранимые системой о каждом файле. Более подробно об этом написано в главе 10.Как предупредить пользователя, если он сбился с пути Давайте посмотрим, как можно заставить систему посылать сообщение электронной почты всякий раз, когда пользователь указывает свое секретное слово неверно. Нам нужно модифицировать тольк Почтовое сообщение будет послано вам в том случае, если вы вставите свой адрес электронной почты там, где в программе записано YOUR_AD-DRESS_HERE. Все, что нам нужно для этоячЬ good word (my($sornename,$someguess) = @ ; # перечислить параметры •^somename =~ s/\W.*//; t удалить все симИ первого слова * Если точнее, то в дескриптор файла stderr, но обычно это и означает терминал.$somename =~ tr/A-Z/a-z/; # перевести все символы в нижний регистр if ($somename eq "randal") ( # не нужно угадывать elsif (($words($somename}ll"groucho") eq $someguess) { return 1; ff возвращаемое значение — true else ( open MAIL, "Imail YOUR_ADDRESS_HERE"; print MAIL "bad news: $somename guessed $someguess\n"; close MAIL; return 0; 4f возвоашаемое значение — falпервый новый оператор здесь — open, в начале второго аргумента котоСледующий оператор, print, выбирает для вывода не stdout*, а дескриптор файла, стоящий между ключевым словомНаконец, мы закрываем дескриптор файла, в результате чего запускается программа mail и передает свои данные.Чтобы соблюсти все формальности, мы могли бы посылать не только ошибочный, но и правильный ответ, но тогда тот, кто заглядывает нам через плечо (или прячется в системе электронной почты), когда мы читаем сообщения, получил бы слишком мн Perl может также открывать дескрипторы файлов, вызывать команды с необходимыми аргументами, даже порождать копию текущей программы и выполнять две (или более) копии программНесколько файлов секретных слов в текущем каталоге Давайте слегка изменим способ определения имени файла секретных слов. Вместо файла с именем wordslist будем искать в текущecho *. secret " Говоря техническим языком — выбранный в текущий момент дескриптор файла. udКак вы скоро увидите, Perl применяет похожий синтаксис имен с использованием метасимволов. Еще раз вернемся к определению;ub init words ( while ( defined($filename = glob("*.secret")) ) ( open (WORDSLIST, $filename) 11 die "can't open wordlist: $!"; f (-M WORDSLIST >= 7.0) ( while ($name = <WORDSLIST” ( chomp $name; Sword = <WORDSLIST>; chomp $word; Swords ($name ) = $word; ) ) close (WORDSLIST) II die "couldn't close wordlist: $!"; Сначала мы поместили в новый цикл while основную часть подпрограммы из предыдущей версии. Новый элемент здесьТаким образом, если текущий каталог содержит файлы fred. secret и sarney.secret, то при первом выполнении циклВ ходе выполнения цикла while мы открываем файл и проверяем, достаточно ли давно он обновлялся (с момента последнего изменения должно пройти не более семи дней). С этими досОтметим, что в отсутствие файлов, имена которых совпадали бы с шаблоном *. secret и "возраст" которых не превышал бы семи дней, подпрограмма завершится, не поместив ни одног* Да-да, опять undef. /.Как получить список секретных слов Итак, Главный Директор Списков Секретных Слов желает получить отчет обо всех секретных словах, используемых в текущий момент, с указанием их "возраста". Если мы на минутку расстанемся с Сперва давайте получим все наши секретные слова, воспользовавшись для этого частью кода из подпрограммы init_words ( ) :while ( defined($filename = glob("*.secret")) ) ( open (WORDSLIST, $filename) II die "can't open wordlist: $!"; if (-M WORDSLIST >= 7.0) < while ($name - <WORDSLIST” ( chomp ( $name ) ; $word = <WORDSLIST> ; chomp (Sword) ; *** отсюда начинается новый код }) I close (WORDSLIST) 11 die "couldn't close wordlist: $!" К моменту достижения того места программы, где дан комментарий "отсюда начнется новый код", мы знаем три вещи: имя файла (содержится в переменной $filename),format STDOUT = @“““““<““ @<““““ @“““““< $filename, $name, $word Определение формата начинается строкой format stdout=, а завершается точкой. Две строки между первой строкой и точкойВызывается определенный таким образом формат функцией write, например:*! /usr/bin/perl while ( defined($filename = glob("*.secret")) ) ( open (WORDSLIST, $filename) 11 die "can't open wordlist: $"'; ir (-M WORDSLIST >= i.u) { while ($name = <WORDSLIST>)close (WORDSLIST) II die "couldn't close wordlist: $!"; } format STDOUT ” @ <<<<<<<<<<<<<<< @<<<<<<<<<<Когда вызывается формат . Perl вычисляет выражения, имеющиеся в строке значений, и генерирует строку, которую передает в дескрипторГм-м. Мы забыли дать столбцам названия. Впрочем, это достаточно легко сделать. Нужно просто ввести формат начала страницы: format STDOUT_TOP = Page @“ $% Filename Name Word Этот формат называется stdout_top; он будет использоваться при первом вызове формата stdout, В первой строке заголовка стоит неизменяемый текст (Page) и трехзначный определитель поля. Следующая строка —Третья строка формата пуста. А поскольку она не содержит никаких полей, то следующая за ней строка тоже пустая; она копируется прямо на вывод, вследствие чего между идущими ниже номером страницы и заголовками столбцов появляется пустая Последние две строки формата также не содержат никаких полей, поэтому они копируются на вывод в том виде, в каком записаны. Таким образом, этот формат обеспечивает создание четырех строк, одна из которых меняется от страницы к странице. * Благодаря модулю English можно использовать для этих предопределенных скалярных переменных более мнемоническиЧтобы это опредиление заработало, попробуйте присоеденить его к преды-дущей программе. Perl отыщет формат начала страницы автоматически. В Perl имеются также поля, которые центрируються и выравниваются по правому краю области вывода. Этот язык кроме тогоподдерживает одновременное выравнивание и по правому и по левому краям. Подпобно об этом мы поговорим когда дойдем до форматов , в главе 11. Как сделать старые списки слов более заметными Просматривая файлы *.secret в текущем каталоге, мы, возможно, обнаружим слишком старые файлы. До сих пор мы пВот как выглядит подпрограмма init_words () , модифицированная для выполнения такой операции:sub init_words { while ($filename = <*.secret>) { open (WORDSLIST, $filename) || die "can't open $filename: $!"; if (-M WORDSLIST < 7) { while ($name = <WORDSLIST>) { chomp ($name) ; $word = <WORDSLIST> ; chomp ($word); $words {$name} = $word ; } } else { # rename the file so it gets noticed rename ($filename, "$filename.old") || die "can't rename $filename.old: $!"; } } Обратите внимание на новую часть оператора else в блоке проверки "возраста" файлов. Если файлВ Perl имеет полный набор операций, необходимый для манипулирования файлами; все, что можно сделать с файлом в С-программе, можно сделать с ним в Perl.
52 Изучаем PerlВедение базы данных о времени правильного ввода секретных слов Давайте проследим за тем, когда каждый пользователь последний раз правильно вводил свой вариант секретного слова. Очевидно, единственная структура данных, которая годится для этого $last_good{$name} == time; присваивает значение текущего времени, выраженное во внутреннем формате (некоторое большое целое, свыше При этом, однако, в промежутках между вызовами программы хеш не существует. Каждый раз, когда программа вызывается, формируется новый хеш, т.е. по большому счету мы всякий раз создаем одноэлементный хеш, а по завершении выполнения прогр
Функция dbmopen (%last_good,"lastdb",0666) || die "can't dbmopen lastdb: $!", $last_good{$name} = time; dbmclose (%last_good) 11 die "can't dbrnclose lastdb: $!"; Первый оператор выполняет отображение, используя имена файлов lastdb. dir и lastdb.pagВторой оператор показывает, что мы используем этот преобразованный хеш как первоначальный. При этом, однако, при создании или корректировке какого-либо элемента хеша автоматически обновляются файлы, образующие DBM-файл. При последующем* Можно также использовать низкоуровневую функцию tie с конкретной базой данных;этот вариант подробно описан в главах 5 и 7 книги Programming Perl** Фактически права доступа к этим файлам определяются в результате выполнения логической операции И над числом 0666 и текущим зна7. Введение 53механизму хеш физически существует и в периоды между вызовами данной программы. Третий оператор отсоединяет хеш от DBM-файла, делая это практически так же, как операция close закрывает файл.Хотя все вновь введенные нами операторы позволяют вести базу данных (и даже обеспечивают ее создание), у нас пока нет никакого способа получить имеющуюся в этой базе данных информацию. Для этого придется создать отдельную программку, ко #!/usr/bin/peri dbmopen (%last_good, "lastdb", 0666) I I die "can't dbmopen lastdb: $!"; foreach $name (sort keys (%last_good) ) { $when = $last_good($name); $hours = (timed - $when) / 3600; # вычислить истекшее время в часахwrite; } format STDOUT = User @<<<<<<: last correct guess was @“< hours ago. $name, $hours Здесь мы осуществляем несколько новых операций: выполняем цикл foreach, сортируем список и получаем значения ключей массива.Сначала функция keys принимает имя хеша в качестве аргумента и возвращает список значений всех ключей этого хеша в произвольном порядке. Для хеша %words,fred, barney, betty, wiima, причем имена могут быть перечислены в произвольном порядке. В случае хеша %last_goodФункция sort сортирует этот список в алфавитном порядке (как если бы вы пропустили текстовый файл через фильтр sort).Уже упомянутый Perl-оператор foreach очень похож на оператор foreach shell С. Он получает список значений и прПри выполнении цикла foreach происходит загрузка пары переменных, используемых затем в формате stdout, и вызыв
54 Изучаем PerlВ Perl используются также удобные способы создания и ведения текстовых баз данных (например, файла паролей) и баз данных с фикОкончательные варианты программ Здесь вашему вниманию предлагаются программы, которые мы писалрт в этой главе, в окончательном виде. Сначала — программа-п^!/usr/bin/peri &init_words ( ) ; print "what is your name? " ; $name = <STDIN>; chomp ($name) ; if ($name =~ /^randalVb/i) { # } else { print "Hello, $name! \n"; # $guess = <STDIN> ; chomp $ guess ; while (! good_word( $name, $guess)) { print "Wrong, try again. What is the secret word? "; $guess = <STDIN> ; chomp $guess; } } dbmopen (%last_good, "lastdb", 0666); $last_good{$name} = time; dbmclose (%last_good); sub init_words { while ($filename = <*.secret>) { open (WORDSLIST, $filename) || die "can't open $filename: $!"; if (-M WORDSLIST < 7) { while ($name = <WORDSLIST>) { chomp ($name) ; $word = <WORDSLIST> ; chomp ($word); $words {$name} = $word ; } } else { # rename the file so it gets noticed rename ($filename, "$filename.old") || die "can't rename $filename.old: $!"; } close WORDSLIST; } } sub good_word { my($somename,$someguess) = @_; # $somename =~ s/\W.*//; # удалить все символы, стоящие после первого слова$somename =~ tr/A-Z/a-z/; # перевести все символы в нижний регистрif ($somename eq "randal") { # не нужно угадывать
1. Введение 55return 1; return 1; # возвращаемое значение — true } else {open MAIL, "| mail YOUR_ADDRESS_HERE"; print MAIL "bad news: $somename guessed $someguess\n"; close MAIL; return 0; # возвращаемое значение — false ”Теперь — листер секретных слов: #!/usr/bin/peri while ($filename = <*.secret>) ( open (WORDSLIST, $filename) I I die "can't open $filename: $!"; if (-M WORDSLIST < 7) { while ($name = <WORDSLIST>) ( chomp ($name) ; $word = <WORDSLIST> ; chomp ($word) ; write; # format STDOUT = @<“““““““ @““<““ @““““<“ $ filename, $name, $word format STDOUT_TOP = Page @“ $% Filename Name Word И, наконец, программа выдачи времени последнего правильного ввода пароля: #!/usr/bin/peri dbmopen (%last good, "lastdb", 0666); foreach $name (sort keys %last_good) ( $when = $last_good ($name); $hours = (time - $when) / 3600; # вычислить истекшее время в часахwrite; } format STDOUT = User @<“““““: last correct guess was @“< hours ago. $name, $hours . 56 Изучаем Perl
Добавьте к этим программам списки секретных слов (файлы с именами что-то. secret, находящиеся в текущем каталоге) и базу данУпражнение Большинство глав завершаются упражнениями, ответы к которым даются в приложении А. Для этой главы ответы уже были даны выше. 1. Наберите программы-примеры и заставьте их работать. (Вам понадобится создать списки секретных слов.) Если потребуется помощь — |
| Содержание | Предисловие | В |