468x60comp.hardware.ani.


| Содержание | Предисловие | В

Глава 1

    В этой главе:


Введение

История создания языка Perl

Слово Perl является аббревиатурой выражения Practical Extraction and Report Language

Позабавившись немного с этой версией, добавив кое-что, Ларри предложил ее сообществу читателей материалов телеконференций Usenet,

В результате Perl все рос и рос, причем почти с той же скоростью, что и операционная система UNIX. (Специально

Повысилась переносимость. То, что когда-то было компактным языком, теперь сопровождается сотнями страниц документации, в состав которой входят десятки man-страниц, Ларри уже не сопровождает Perl в одиночку, но сохраняет свой эксклюзивный титул главного архитектора. A Perl в

Примеры программ, содержащихся в этой книге, мы проверяли на 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";

* Для получения аналогичного результата можно использовать стандартную подпрограмму libc. Но это не операция.

} 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"; # обычное приветствие print "What is the secret word? "; $guess = <STDIN>; chomp ($guess) ;

$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. Такой конструкции нет ни в одном другом язык

Разные секретные слова для разных пользователей

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

Пользователь

Секретное слово

Fred Barney Betty Wilma

camel llama alpaca alpaca

Обратите внимание: у последних двух пользователей одинаковые секретные слова. Такое допускается.

Самый простой способ сохранить такую таблицу использовать хеш. В каждом элементе хеша содержится отде

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 "Randal") {

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 = $words {$name}; #

$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 = $words($namel; # получить секретное слово if ($sec

$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 обычное приветствие print "What is the secret word? "; $guess = <STDIN>; chomp ($guess);

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) ) (

* На самом деле это опять undef, но для понимания данного материала сказанного достаточно.

Но если бы вы были еще более осторожны, вы, вероятно, проверили бы также и то, возвращает ли функция 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"; # обычное приветствие •print "What is the secret word? "; $guess = <STDIN>; ^homp ($guess) ;

^hile (! good word($name, $guess)) I print "Wrong, try again. What is the secret word? "; $guess = <STDIN>; :homp ($guess);

} ## далее подпрограммы

sub init_words ( open (WORDSLIST, "wordslist") 11

die "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;

присваивает значение текущего времени, выраженное во внутреннем формате (некоторое большое целое, свыше 800 миллионов, число, кото

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

Функция dbmopen* отображает хеш в файл (фактически в пару файлов), известный как DBM-файл. Она использу

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) { # обратно на другой путь :-) 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 (! 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. Введение 55

return 1; # возвращаемое значение — true ) elsif (($wordsf$somename( II "groucho") eq $someguess) (

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 в STDOUT } } close (WORDSLIST) ;

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. Наберите программы-примеры и заставьте их работать. (Вам понадобится создать списки секретных слов.) Если потребуется помощь



|     Назад     |  

| Содержание | Предисловие | В



Сайт создан в системе uCoz