Изучаем Perl

         

Прогулка по стране Perl



Прогулка по стране Perl

Наше путешествие по стране Perl мы начнем с небольшой прогулки. В ходе этой прогулки мы ознакомимся с некоторыми возможностями языка Perl на примере небольшого приложения. Здесь приведены лишь очень краткие пояснения; каждая тема гораздо подробнее освещается в соответствующей главе. Тем не менее эта короткая прогулка должна дать вам возможность быстро "почувствовать" этот язык, и вы сможете решить, будете ли вы дочитывать книгу до конца или же отправитесь обратно к своим Usenet-новостям и лыжным склонам.

Программа Hello, World

Давайте рассмотрим небольшую программу, которая делает что-то реальное. Вот ваша базовая программа, которая выводит на экран слова "Hello, World":

^! /usr/bin/perl -w print ("Hello, World\n") ;

Первая строка говорит о том, что это программа написана на языке Perl. Кроме того, первая строка является комментарием; ведь комментарием в Perl, как и во многих интерпретирующих языках программирования, являются все лексемы, стоящие после знака # и до конца текущей строки. Но, в отличие от всех остальных комментариев, включенных в эту программу, комментарий в первой строке особенный: Perl ищет здесь необязательные аргументы. В данном случае использовался ключ -w. Этот очень важный ключ дает Perl указание выдавать дополнительные предупреждающие сообщения о потенциально опасных конструкциях. Вам следует всегда использовать в своих программах ключ -w.

Вторая строка — это выполняемая часть данной программы. Здесь мы видим функцию print. В данном случае встроенная функция print имеет всего один аргумент, С-подобную текстовую строку. В этой строке комбинация символов \п обозначает символ новой строки. Оператор print завершается точкой с запятой (;). Как и в С, все простые операторы в Perl завершаются точкой с запятой*.

Когда вы вызываете эту программу, ядро запускает интерпретатор Perl, который разбирает всю программу (обе строки, включая первую, т.е. комментарий), а затем выполняет компилированный вариант. Первая и единственная операция — выполнение функции print, которая посылает значения своих аргументов на устройство вывода. По окончании выполнения программы этот Perl-процесс завершается и возвращает родительскому shell код успешного выполнения.

* Точку с запятой можно опустить, если оператор является последним оператором в блоке или файле либо оператором eval.

Скоро вы увидите Perl-программы, в которых print и другие функции иногда вызываются с круглыми скобками, а иногда — без них. Правило здесь простое: круглые скобки для встроенных функций Perl не являются ни обязательными, ни запрещенными. Их применение может прояснить ситуацию, а может и запутать ее, так что выработайте собственный стиль.

Как задавать вопросы и запоминать результат

Давайте немного усложним пример, ведь приветствие Hello, World — слишком простое и статичное. Сделаем так, чтобы программа называла вас по имени. Для этого нам нужно место для хранения имени, способ задания вопроса об имени и способ получения ответа.

Одно из мест для хранения значений (вроде имени) — скалярная переменная. Для хранения вашего имени в нашей программе мы используем скалярную переменную $name. В главе 2, "Скалярные данные", мы более подробно узнаем о том, что можно хранить в этих переменных и что можно с ними делать. Пока же предположим, что мы можем хранить в скалярной переменной только одно число или строку (последовательность символов).

Программа должна иметь возможность спросить у вас имя. Для этого нам нужен способ выдачи приглашения и способ принятия от вас данных. Предыдущая программа показала нам, как можно приглашать, используя для этого функцию print. Получение же строки с терминала осуществляется с помощью конструкции <stdin>, которая (в нашем случае) получает одну строку введенных данных. Введенные данные мы присваиваем переменной $ name. Это дает нам следующую программу:

print "What is your name? "; # Как ваше имя? $name = <STDIN>;

Значение переменной $name пока содержит завершающий символ новой строки (имя Randai поступает как Randal\n). Чтобы избавиться от этого символа, мы воспользуемся функцией chomp, которая в качестве своего единственного аргумента принимает скалярную переменную и удаляет из ее строкового значения завершающий символ перехода на новую строку (пробельный символ), если он присутствует:

chomp ($name) ;

Теперь нам нужно лишь сказать Hello и указать значение переменной $name, что мы можем сделать так, как в shell, поместив эту переменную в заключенную в кавычки строку:

orint "Hello, $name ! \n";



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

Сложив все вместе, получаем:

•ft! /usr/bin/perl -w print "What is your name? " ; $name = <STDIN>; chomp ($name) ; print "Hello, Sname ' \n" ;

Добавляем возможность выбора

Допустим теперь, что у нас припасено какое-то особое приветствие для пользователя по имени Рэндал, а для остальных — обычное. Для этого нам нужно сравнить имя, которое было введено, со строкой Randal, и, если оно совпадает, сделать что-то особое. Давайте добавим в программу С-подобную ветвь tf-then-eise и операцию сравнения:

#!/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 сравниваются две строки. Если они равны (т.е. совпадают все символы и длина строк одинакова), результатом будет "истина". (В С и C++ подобной операции нет*).

Оператор 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. После приветствия программа спрашивает (посредством вызова еще одной функции print) у пользователя (не Рэндала) его вариант секретного слова. Этот вариант сравнивается с заданным секретным словом в операции ne. Данная операция возвращает значение "истина", если сравниваемые строки не равны (т.е. данная операция противоположна операции eq). Результат сравнения управляет циклом while, который выполняет этот блок операторов до тех пор, пока сравнение дает значение "истина".

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

Несколько секретных слов

Давайте посмотрим, как можно модифицировать эту программу так, чтобы она принимала несколько секретных слов. Используя то, что мы уже видели, можно было бы многократно сравнивать вариант-догадку с рядом правильных ответов, хранящихся в отдельных скалярных переменных. Такой список, однако, было бы трудно корректировать или модифицировать в зависимости от дня недели и даты.

Более эффективное решение — хранить все возможные ответы в структуре данных, которая называется список, или (предпочтительнее) массив. Каждый элемент массива — это отдельная скалярная переменная, которой можно присваивать значение и затем использовать ее независимо от других. Можно также одним махом присвоить значение всему массиву. Мы имеем право присвоить значение всему массиву с именем @words так, чтобы он содержал три возможных правильных пароля:

@words = ("camel", "llarna", "alpaca");

Имена переменных-массивов начинаются с символа @, что позволяет отличать их от имен скалярных переменных. Существует еще один способ записи этой конструкции так, чтобы не нужно было ставить все эти кавычки — с помощью операции qw ( ) , например:

@words = qw (camel llama alpaca) ;

Это абсолютно то же самое; операция qw работает так, как будто мы взяли в кавычки каждую из трех строк.

Присвоив значения элементам массива, мы можем обращаться к каждому из них по индексной ссылке. Так, $words [0] — это camel, $words [1] — llama, a $words [2] — alpaca. Индекс может быть и выражением, поэтому если мы присвоим $i значение 2, то элементом $words [$i] будет alpaca. (Индексные ссылки начинаются с символа $, а нес @, потому что они обозначают один элемент массива, а не весь массив.) Вернемся к нашему предыдущему примеру:

#! /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 { # больше слов нет, должно быть, неверно print "Wrong, try again. What is the secret word?"; $guess = <STDIN>; chomp ($guess) ;

$i = 0; # вновь начать проверку с первого слова }

} # конец цикла while для неверных слов } # конец цикла "не Рэндал"

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

В этой программе показан также блок eisif оператора if-then-eise. Такой конструкции нет ни в одном другом языке программирования; это сокращенная запись блока else с новым условием if, но без вложения еще одной пары фигурных скобок. Сравнение набора условий в каскадной цепочке if-eisif-eisif-eisif-eise очень характерно для языка Perl. В нем нет эквивалента оператору switch языка С или оператору case языка Паскаль, но вы можете сами без особых хлопот создать такой оператор. Подробности см. в главе 2 книги Programming Perl и на странице руководства perlsyn(1).

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

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

Пользователь Секретное слово
Fred Barney Betty Wilma camel llama alpaca alpaca

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

Самый простой способ сохранить такую таблицу — использовать хеш. В каждом элементе хеша содержится отдельное скалярное значение (как и в массиве любого другого типа), но в соответствие каждому элементу хеша ставится ключ. Ключом может быть любое скалярное значение (любая строка или число, в том числе нецелые и отрицательные числа). Чтобы создать хеш под именем Swords (обратите внимание на то, что используется символ ^ вместо @) с ключами и значениями, данными в приведенной выше таблице, мы присвоим Swords значение (почти так же, как мы делали раньше с массивом):

Swords = qw( fred camel barney llama betty alpaca wilma alpaca

);

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

Чтобы найти секретное слово для Betty, мы должны использовать имя Betty как ключ в ссылке на хеш Swords с помощью выражения вроде $words { "betty"}. Значение этой ссылки - alpaca, это похоже на то, что мы видели раньше, работая с другим массивом. Как и раньше, ключом может быть любое выражение, поэтому установка $person в значение betty и вычисление $words { $person} также дает в результате alpaca.

Сведя все это воедино, мы получаем такую программу:

#! /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}; # получить секретное слово print "What is the secret word? "; $guess = <STDIN>; chomp ($guess) ; while ($guess ne $secretword) {

print "Wrong, try again. What is the secret word? "; $guess = <STDIN>; chomp ($guess) ;

} }

Обратите внимание на то, как происходит поиск секретного слова. Если имя не найдено, то значением переменной $secretword будет пустая строка*, и мы можем использовать оператор if, чтобы задать секретное слово по умолчанию для кого-нибудь еще. Вот как это выглядит:

[ . .. остальная часть программы удалена . . . ] $secretword = $words ($name}; # получить секретное слово

if ( $secretword eq "") { # не найдено

$secretword = "groucho"; # конечно,

#можно использовать

}

print "What is the secret word? "; [. .. остальная часть программы удалена .. . ]

Обработка различных вариантов ввода секретного елова

Если вместо Randai вы введете Randai L. Schwartz или randal, то тем самым лишите Рэндала права на особое приветствие, потому что сравнение eq предполагает точное равенство. Давайте рассмотрим один способ решения задачи обработки различных вариантов ввода.

* На самом деле это значение undef, но для операции eq оно выглядит как пустая строка. Если бы в командной строке вы использовали ключ -w, то получили бы предупреждение на этот счет. Именно поэтому мы его здесь опустили.

Допустим, вы хотите найти все строки, которые начинаются со слова Randal, а не просто строку, равную Randal. В sed, awk или grep это можно сделать с помощью регулярного выражения — шаблона, определяющего совокупность соответствующих строк. Как и в sed, awk или grep, в Perl регулярным выражением, которое соответствует любой строке, начинающейся со слова Randal, будет ^Randai. Чтобы сравнить его со строкой, содержащейся в скалярной переменной $ name, мы используем операцию сопоставления:

if ($name =~ /^Randal/) (

+f да, совпадает ) else ( ## нет, не совпадает

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

Это почти решает нашу задачу, но не позволяет выбрать randal или отклонить Randall. Чтобы принять randal, мы добавляем опцию игнорирования регистра — прописную букву i после закрывающей косой. Чтобы отклонить Randall, мы вводим в регулярное выражение специальный маркер границы слова (подобно тому как это делается в vi и в некоторых версиях grep) в форме \b. Это гарантирует, что символ, следующий в регулярном выражении за первой буквой 1, не является еще одной буквой. В результате наше регулярное выражение принимает вид /^randal\b/i, что означает "слово randal, стоящее в начале строки, за которым нет ни буквы, ни цифры, при этом регистр не имеет значения". Объединив этот фрагмент с остальной частью программы, получим:

#! /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}; # получить секретное слово if ($secretword eq "") { # не найдено

$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.

В Perl имеется все, что необходимо для работы с регулярными выражениями, т.е. он предоставляет все возможности, которые обеспечивает любая стандартная утилита UNIX (и даже некоторые нестандартные). Способ сопоставления строк, используемый в Perl, является, чуть ли не самым быстрым сравнительно с другими языками, поэтому производительность системы при выполнении Perl-программ никоим образом не снижается. (Написанная на Perl grep-подобная программа часто превосходит прилагаемую поставщиками программу grep на С*. Это значит, что grep не выполняет толком даже единственную свою задачу.)

Справедливость для всех

Итак, теперь я могу ввести Randal, randal или Randal L. Schwartz, но как быть с остальными? Барни должен вводить в точности barney (ему нельзя ввести даже пробел после barney).

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

Сначала — операция подстановки: мы хотим взять содержимое переменной $ name, найти первый специальный (не использующийся в словах) символ и убрать все символы, начиная с того места, где он стоит, и до конца строки. Искомое регулярное выражение имеет вид /\w.*/. Здесь \w обозначает специальный символ (т.е. все кроме буквы, цифры и знака подчеркивания), а . * обозначают любые символы с этого места до конца строки. Чтобы убрать эти символы, нужно взять ту часть строки, которая совпадает с рассматриваемым регулярным выражением, и заменить ее пустой строкой:

$name =~ s/\W.*//;

Мы используем ту же операцию =~, что и раньше, но справа у нас теперь стоит операция подстановки — буква s, за которой следуют заключенные между двумя косыми регулярное выражение и строка. (Строка в данном

Однако GNU-версия утилиты egrep выполняет эту операцию гораздо быстрее, чем Perl.

примере — это пустая строка между второй и третьей косыми.) Эта операция выглядит и выполняется во многом так же, как операции подстановки в программах-редакторах.

Теперь для того, чтобы перевести все оставшиеся символы в нижний регистр, мы преобразуем эту строку с помощью операции tr*. Она очень похожа на UNIX-команду tr, т.е. получает список искомых символов и список символов, которыми искомые символы заменяются. В нашем примере мы, чтобы перевести содержимое переменной $name в нижний регистр, используем такую запись:

$name =~ •tr/A-Z/a-z/;

Между косыми заключены списки искомых и заменяющих их символов. Дефис между буквами а и z обозначает все символы, находящиеся между ними, т.е. у нас есть два списка, в каждый из которых включено по 26 символов. Когда tr находит символ из какой-либо строки первого списка, он заменяется соответствующим символом из второго списка. В результате все прописные буквы А, В, С и т.д. становятся строчными**. Объединяя эти строки с остальной частью программы, получаем:'

#!/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 ($secretword eq "") ( # не найдено

$secretword == "groucho"; 4 конечно, можно использовать }

print "What is the secret word? "; $guess = <STDIN>; chomp ($guess); while ($guess ne $secretword) (

* Символы с диакритическими знаками такому переводу не поддаются. Подробности см. на man-странице рег11оса1е(\) в версии языка Pel-15.004.

** Специалисты заметят ,что мы также могли написать нечто вродез/(\3*) .*/\L$1/, чтобы сделать все это за один присест, но специалисты, вероятно, не будут читать данный раздел.

print "Wrong, try again. What is the secret word? "; $guess = <STDIN>; chomp ($guess);

Обратите внимание на то, что сопоставление с регулярным выражением для слова Randai вновь выполняется с помощью обычной операции сравнения. Дело втом, что и RandaL L. Schwartz, и Randai после подстановки и перевода превращаются в randai. Для всех остальных пользователей справедливо то же самое, потому что Fred и Fred Flinstone превращаются во fred; Barney Rubbie И Barney, the little guy — В barney И Т.Д.

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

Отметим, однако, что в процессе обработки имени (т.е. при его модификации, необходимой для проведения операции сравнения и поиска соответствия в таблице) первоначально введенное имя уничтожается. Поэтому перед обработкой имени программа сохраняет его в переменной $original name. (Как и имена в С, имена переменных в Perl состоят из букв, цифр и знаков подчеркивания, причем длина их практически не ограничена.) Благодаря этому мы впоследствии сможет ссылаться на $ original name.

В Perl имеется много способов, позволяющих проводить анализ и изменение символов в строках. С большинством из них вы познакомитесь в главах 7 и 15.

Повышение степени модульности

Теперь, когда мы добавили так много строк к нашему первоначальному коду, нам при его просмотре будет непросто уловить общую логику построения программы. Поэтому было бы неплохо отделить высокоуровневую логику (запрос имени, циклы, используемые для обработки введенных секретных слов) от низкоуровневой (сравнение введенного секретного слова с заданным). Это необходимо сделать, например, для облегчения понимания программы другими пользователями, или по той причине, что один человек пишет высокоуровневую часть, а другой — низкоуровневые фрагменты.

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

Первая строка в данном конкретном определении — это операция присваивания, с помощью которой значения двух параметров подпрограммы копируются в две локальные переменные с именами $somename и $someguess (директива ту ( ) определяет эти переменные как локальные для блока, в который они входят (в данном случае для всей подпрограммы), а параметры первоначально находятся в специальном локальном массиве с именем @ .)

Следующие две строки удаляют символы, стоящие после имени (точно так же, как в предыдущей версии программы).

Оператор if-elsif-else позволяет определить, является ли введенный пользователем вариант слова ($someguess) верным для имени ($somename). Имя Randal не должно попасть в эту подпрограмму, но даже если и попадет, то любой вариант его ввода будет принят как правильный.

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

Проверка в части eisif выглядит довольно сложной; давайте разобьем ее на фрагменты:

($words($somename) И "groucho") eq $someguess

Первый элемент в круглых скобках обеспечивает проведение уже знакомого нам хеш-поиска, в результате которого отыскивается некоторое значение в массиве %words на основании ключа, полученного из массива $somename. Знак 1 1, стоящий между этим значением и строкой groucho, обозначает операцию ИЛИ, аналогичную той, что используется в языке С, awk и в различных shell. Если поиск в хеше даст некоторое значение (это

значит, что ключ $ some name находился в хеше), то оно и будет являться значением данного выражения. Если ключ найден не был, то используется строка groucho. Это весьма характерно для Perl: приводится некоторое выражение, а затем с помощью операции 1 1 для него указывается значение по умолчанию на тот случай, если результатом поиска является значение "ложь".

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

Выразим все это в виде правила: если имя — randal или если вводимый пользователем вариант соответствует одному из имен, находящемуся в массиве %words (со значением по умолчанию groucho, если имя в массиве не найдено), то подпрограмма возвращает 1; иначе подпрограмма возвращает 0. Теперь давайте свяжем все новые строки с остальной частью программы:

#! /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 и $auess. Значение $somename устанавливается равным первому параметру,

в данном случае $name. Аналогичным образом $someguess передается во втором параметре, $guess.

Значение, возвращаемое этой подпрограммой (1 или 0, если вы помните приведенное выше правило), логически инвертируется префиксной операцией ! (логическое НЕ). Эта операция возвращает значение "истина", если следующее за ней выражение ложно, или "ложь", если оно истинно. Результат этой операции управляет циклом while. Можете читать такую запись как "до тех пор, пока слово угадано неправильно...". Многие хорошо написанные Perl-программы очень похожи на обычный английский язык — если, конечно, не позволять себе много вольностей ни с языком Perl, ни с английским. (Но Пулитцеровскую премию даже за очень хорошую программу вам не дадут.)

Обратите внимание: при разработке этой подпрограммы предполагалось, "то значение хеша %words задается в основной программе.

Столь деликатный подход к использованию глобальных переменных вызван тем, что обращаться с ними нужно очень аккуратно. Говоря в общем, переменные, не созданные с помощью ту, глобальны для всей программы, тогда как переменные ту действуют только до завершения выполнения блока, в которым они были объявлены. Но не беспокойтесь: в языке Perl имеется множество других разновидностей переменных, включая переменные, локальные для файла (или пакета), и переменные, локальные для функции, которые сохраняют свои значения от вызова к вызову — а это как раз то, что мы могли бы здесь использовать. Впрочем, на данном этапе вашего знакомства с Perl изучение этих переменных только осложнило бы вам жизнь. Когда вы будете достаточно готовы к этому, посмотрите, что говорится о контекстах, подпрограммах, модулях и объектах в книге Programming Perl, или обратитесь к диалоговой документации, имеющейся на man-страницах perlsub(\), perlmod(l), perlobJ(l) и perltoot(l).

Перенос списка секретных слов в отдельный файл

Допустим, вы хотели бы использовать список секретных слов в трех программах. Если вы сохраните этот список так, как мы уже это делали, нам придется корректировать все три программы (если, например, Бетти решит, что ее секретным словом должно быть не alpaca, a swine). Это может стать настоящим кошмаром, особенно если Бетти отличается непостоянством.

Поэтому давайте поместим список слов в файл, а затем, чтобы ввести список в программу, просто прочитаем файл. Для этого нужно создать канал ввода-вывода, который называется дескриптором файла. Ваша Perl-программа автоматически получает три дескриптора файлов, stdin, stdout и stderr, которые соответствуют трем стандартным каналам ввода-вывода в большинстве сред программирования. Мы уже используем дескриптор stdin для чтения данных, поступающих от пользователя, запускающего нашу программу. Теперь нужно просто создать для выбранного нами файла другой дескриптор.

Это делается с помощью следующего кода:

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, связывая его с файлом wordslist, находящимся в текущем каталоге. Отметим, что перед этим дескриптором не ставится никакого забавного символа, вроде тех трех, что предваряют наши переменные. Кроме того, дескрипторы файлов обычно записываются прописными буквами (хотя это и не обязательно); причины этого мы рассмотрим позднее.

При выполнении цикла while читаются строки из файла wordslist (через дескриптор файла wordslist) по одной при каждом проходе цикла. Каждая строка заносится в переменную $name. По достижении конца файла операция <wordslist> возвращает пустую строку*, которая для цикла while означает "ложь", и завершает цикл.

Если бы вы выполняли программу с ключом -w, вам пришлось бы проверять, определено ли полученное возвращаемое значение. Пустая строка, которую возвращает операция <wordslist>, не совсем пуста: это опять значение undef. В тех случаях, когда это важно, проверка выражения на значение undef производится функцией defined. При чтении строк из файла эта проверка выполнялась бы следующим образом:

while ( defined ($name = <WORDSLIST) ) (

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

Но если бы вы были еще более осторожны, вы, вероятно, проверили бы также и то, возвращает ли функция open значение "истина". Это, кстати, в любом случае неплохая идея. Для выхода из программы с сообщением об ошибке в случае, если что-то работает не так, часто используется встроенная функция die. Мы рассмотрим пример этой функции в следующей версии нашей программы.

С другой стороны, при нормальном развитии событий мы считываем строку (включая символ новой строки) в переменную $ name. Сначала с помощью функции chomp убирается символ новой строки, затем нужно прочитать следующую строку, чтобы получить секретное слово и сохранить sro в переменной $word. Символ новой строки при этом тоже убирается.

Последняя строка цикла while помещает $word в %words с ключом $name, чтобы переменную $word могла использовать остальная часть программы.

По завершении чтения файла его дескриптор можно использовать повторно, предварительно закрыв файл с помощью функции close. (Дескрипторы файлов автоматически закрываются в любом случае при выходе из программы, но мы стараемся быть аккуратными. Однако если бы мы были по-настоящему аккуратными, мы бы даже проверили бы, возвращает ли ^lose значение "истина" в случае, если раздел диска, в котором был файл, решил "отдохнуть", если сетевая файловая система стала недосягаемой или произошла еще какая-нибудь катастрофа. Такое ведь иногда случается. Законы Мерфи никто не отменял.)

Сделанное выше определение подпрограммы может идти после другого аналогичного определения или перед ним. Вместо того чтобы помещать определение %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 возвращаемое значение — false

Теперь написанный нами код начинает выглядеть как настоящая "взрослая" программа. Обратите внимание: первая выполняемая строка — вызов подпрограммы init_word() . Возвращаемое значение в последующих вычислениях не используется, и это хорошо, потому что мы не возвратили ничего заслуживающего внимания. В данном случае это гарантированно значение "истина" (в частности, значение 1), потому что если бы close не выполнилась, то die вывела бы сообщение в stderr и вышла из программы. Функция die подробно описывается в главе 10, но поскольку очень важно проверять возвращаемые значения всего, что может завершиться неудачно, мы возьмем за правило использовать эту функцию с самого начала. Переменная $! (тоже рассматривается в главе 10) содержит системное сообщение об ошибке, поясняющее, почему данный системный вызов завершился неудачно.

Функция open используется также для открытия файлов при выводе в них информации и открытия программ как файлов (здесь она лишь упомянута). Полное описание этой функции будет дано гораздо позже, в главе 10.

Как обеспечить скромный уровень безопасности

'Этот список секретных слов должен меняться минимум раз в неделю!", — требует Главный Директор Списков Секретных Слов. Мы не можем, конечно, заставить пользователей еженедельно менять пароли, но должны хотя бы предупреждать их о том, что список секретных слов не изменялся в течение семи дней и более.

лучше всего делать это в подпрограмме init_words ( ) ; мы уже раоотаем в ней с файлом wordlist. Perl-операция -м возвращает значение, равное количеству дней, прошедшему с момента изменения файла или дескриптора файла, поэтому нам нужно просто посмотреть, превышает ли это значение число семь для дескриптора файла wordslist:

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. Если оно больше, то мы, выходит, нарушили правила. Здесь мы видим новую операцию, операцию die, которая одним махом выводит сообщение на экран* и прерывает программу.

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

Помимо определения "возраста" файла мы можем узнать имя его владельца, размер, время последнего доступа к нему и все остальные сведения, хранимые системой о каждом файле. Более подробно об этом написано в главе 10.

Как предупредить пользователя, если он сбился с пути

Давайте посмотрим, как можно заставить систему посылать сообщение электронной почты всякий раз, когда пользователь указывает свое секретное слово неверно. Нам нужно модифицировать только подпрограмму good word ( ) (сказывается преимущество модульности языка Perl), потому что вся необходимая информация находится у нас там.

Почтовое сообщение будет послано вам в том случае, если вы вставите свой адрес электронной почты там, где в программе записано YOUR_AD-DRESS_HERE. Все, что нам нужно для этого сделать, это непосредственно перед тем, как возвращать из подпрограммы 0, создать дескриптор файла, который фактически будет являться процессом (mail):

ячЬ good word (

my($sornename,$someguess) = @ ; # перечислить параметры •^somename =~ s/\W.*//; t удалить все символы, стоящие после

И

первого слова

* Если точнее, то в дескриптор файла stderr, но обычно это и означает терминал.

$somename =~ tr/A-Z/a-z/; # перевести все символы в нижний регистр if ($somename eq "randal") ( # не нужно угадывать return 1; # возвращаемое значение — true

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 возвоашаемое значение — false

первый новый оператор здесь — open, в начале второго аргумента которого стоит оператор канала (1). Он указывает, что мы открываем процесс, а не файл. Поскольку оператор канала находится перед именем команды, мы открываем процесс так, чтобы можно было осуществить в него запись. (Если поставить оператор канала в конец, а не в начало, то можно будет читать выходную информацию команды.)

Следующий оператор, print, выбирает для вывода не stdout*, а дескриптор файла, стоящий между ключевым словом print и подлежащими выводу на экран значениями. Это значит, что сообщение в конечном итоге станет входной информацией для команды mail.

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

Чтобы соблюсти все формальности, мы могли бы посылать не только ошибочный, но и правильный ответ, но тогда тот, кто заглядывает нам через плечо (или прячется в системе электронной почты), когда мы читаем сообщения, получил бы слишком много полезной информации.

Perl может также открывать дескрипторы файлов, вызывать команды с необходимыми аргументами, даже порождать копию текущей программы и выполнять две (или более) копии программы одновременно. Обратные кавычки (как в shell) дают возможность получать результаты работы команды как данные. Все это описывается в главе 14, так что читайте дальше.

Несколько файлов секретных слов в текущем каталоге

Давайте слегка изменим способ определения имени файла секретных слов. Вместо файла с именем wordslist будем искать в текущем каталоге нечто, заканчивающееся на .secret. Попросим shell выдать краткий перечень таких имен.

echo *. secret

" Говоря техническим языком — выбранный в текущий момент дескриптор файла. ud этом, однако, мы поговорим позже.

Как вы скоро увидите, Perl применяет похожий синтаксис имен с использованием метасимволов. Еще раз вернемся к определению подпрограммы init_words () :

;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 основную часть подпрограммы из предыдущей версии. Новый элемент здесь — функция glob. По историческим причинам она называется filename glob. Эта функция работает почти так же, как <stdin>: при каждом обращении к ней она возвращает очередное значение из списка имен файлов, которые соответствуют образцу shell, в данном случае * . secret. Если таких имен файлов нет, возвращается пустая строка*.

Таким образом, если текущий каталог содержит файлы fred. secret и sarney.secret, то при первом выполнении цикла while значением переменной $fiiename будетЬагпеу.secret (именадаютсяпоалфавиту).При втором выполнении цикла значением $filename будет fred .secret. Поскольку при третьем вызове функции glob она возвращает пустую строку, го третий проход не делается, так как цикл while интерпретирует это значение как "ложь", что приводит к выходу из подпрограммы.

В ходе выполнения цикла while мы открываем файл и проверяем, достаточно ли давно он обновлялся (с момента последнего изменения должно пройти не более семи дней). С этими достаточно новыми файлами мы работаем так же, как и раньше.

Отметим, что в отсутствие файлов, имена которых совпадали бы с шаблоном *. secret и "возраст" которых не превышал бы семи дней, подпрограмма завершится, не поместив ни одного секретного слова в массив %words. Это значит, что всем придется пользоваться словом groucho. Прекрасно. (В реальном коде перед выходом из подпрограммы следовало бы ввести операцию проверки количества элементов в массиве %words — и при неудовлетворительном результате выполнить функцию die. Обратите внимание на функцию keys, когда мы дойдем до определения хешей в главе 5.)

* Да-да, опять 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), чье-то имя (в переменной $name) и секретное слово этого человека (содержится в $word). Здесь и нужно использовать имеющиеся в Perl инструменты формирования отчетов. Для этого где-то в программе мы должны определить используемый формат (обычно это делается в конце, как и для подпрограмм):

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>) { chomp ($name); $word = <WORDSLIST> ; chomp ($word) ; write; # вызвать format STDOUT в STDOUT

close (WORDSLIST) II die "couldn't close wordlist: $!"; }

format STDOUT ”

@<<<<<<<<<<<<<<< @<<<<<<<<<< @<<<<<<<<<<<< $filename, $name, $word

Когда вызывается формат. Perl вычисляет выражения, имеющиеся в строке значений, и генерирует строку, которую передает в дескриптор файла stdout. Поскольку write вызывается один раз при каждом проходе цикла, мы получим ряд строк (под одной строке для каждого секретного слова); итоговый текст будет разбит на столбцы.

Гм-м. Мы забыли дать столбцам названия. Впрочем, это достаточно легко сделать. Нужно просто ввести формат начала страницы:

format STDOUT_TOP = Page @“ $%

Filename Name Word

Этот формат называется stdout_top; он будет использоваться при первом вызове формата stdout, а затем через каждые 60 строк, выведенных в stdout. заголовки столбцов позиционируются точно по столбцам формата ^tdout, поэтому все выглядит аккуратно.

В первой строке заголовка стоит неизменяемый текст (Page) и трехзначный определитель поля. Следующая строка — строка значений полей, в данном случае она содержит выражение. Это выражение является переменной $%*, в которой содержится число выведенных страниц.

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

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

*Благодаря модулю English можно использовать для этих предопределенных скалярных переменных более мнемонические псевдонимы легко запоминающиеся названия.

Чтобы это опредиление заработало, попробуйте присоеденить его к преды-дущей программе. Perl отыщет формат начала страницы автоматически.

В Perl имеются также поля, которые центрируються и выравниваются по правому краю области вывода. Этот язык кроме тогоподдерживает одновременное выравнивание и по правому и по левому краям. Подпобно об этом мы поговорим,

когда дойдем до форматов , в главе 11.

Как сделать старые списки слов более заметными

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

Вот как выглядит подпрограмма 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 в блоке проверки "возраста" файлов. Если файл не обновлять семь дней и более, функция rename переименовывает его. Эта функция принимает два параметра и переименовывает файл заданный первым параметром, присваивая ему имя, указанное во втором параметре.

В Perl имеет полный набор операций, необходимый для манипулирования файлами; все, что можно сделать с файлом в С-программе, можно сделать с ним в Perl.

52

Изучаем Perl

Ведение базы данных о времени правильного ввода секретных слов

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

$last_good{$name} == time;

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

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

Функция 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 (это общепринятые имена для файлов lastdb, образующих DBM-файл). Если эти файлы необходимо создать (а это бывает при первой попытке их использования), то для них устанавливаются права доступа 0666**. Такой режим доступа означает, что все пользователи могут читать и осуществлять запись в эти файлы. Если вы работаете в UNIX-системе, то описание битов прав доступа к файлу вы найдете на man-странице chmoc/(2). В других системах chmod() может работать так же, а может и не работать. Например, в MS-DOS для файлов не устанавливаются права доступа, тогда как в Windows NT — устанавливаются. Если уверенности нет, прочтите описание версии вашей системы.

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

* Можно также использовать низкоуровневую функцию tie с конкретной базой данных;

этот вариант подробно описан в главах 5 и 7 книги Programming Perl и на man-страницах perHfc(\) и AnyDMB_File (3).

** Фактически права доступа к этим файлам определяются в результате выполнения логической операции И над числом 0666 и текущим значением переменной umask вашего процесса.

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). Это гарантирует, что список, обрабатываемый следующим далее оператором foreach, всегда будет отсортирован по алфавиту.

Уже упомянутый Perl-оператор foreach очень похож на оператор foreach shell С. Он получает список значений и присваивает каждое из них по очереди какой-либо скалярной переменной (в нашем случае — переменной $name), выполняя для каждого значения по одному разу проход цикла (блок). Так, для пяти имен в списке %last_good мы делаем пять проходов, при этом каждый раз переменная $name имеет другое значение.

При выполнении цикла foreach происходит загрузка пары переменных, используемых затем в формате stdout, и вызывается этот формат. Обратите внимание: мы вычисляем "возраст" файла, вычитая записанное (в массиве) системное время из текущего времени (которое возвращает функция time), а затем деля результат на 3600 (чтобы преобразовать секунды в часы).

54

Изучаем Perl

В Perl используются также удобные способы создания и ведения текстовых баз данных (например, файла паролей) и баз данных с фиксированной длиной записей (таких, как база данных "последней регистрации", которую ведет программа login). Эти способы описаны в главе 17.

Окончательные варианты программ

Здесь вашему вниманию предлагаются программы, которые мы писалрт в этой главе, в окончательном виде. Сначала — программа-приветствие:

^!/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, находящиеся в текущем каталоге) и базу данных lastdb.dir и lastdb .рад, и у вас будет все, что нужно.



Простейшая CGIпрограмма



Простейшая CGI-программа

Вот исходный код вашей первой CGI-программы. Она настолько проста, что в ней даже не пришлось использовать модуль CGI.pm:

#!/usr/bin/perlS -w

#самая легкая из CGI-програми print “END of Multiple Text;

Content-type: text/html

<HTML>

<HEAD>

<TITLE>Hello World</TITLE>

</HEAD>

<BODY>

<Hl>Greetings, Terrans!</Hl>

</BODY </HTML>

END_of_Multiline_Text

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

Зта программка содержит всего один оператор: вызов функции print. Несколько забавно вьп-лядящий аргумент — зто так называемый here-доку-мент. Он состоит из двух знаков "меньше чем" и слова, которое мы назовем конечной лексемой. Для программиста, работающего с shell, написанное, возможно, будет похоже на переадресацию ввода-вывода, но на самом деле зто просто удобный способ взятия в кавычки строкового значення, зани-мающего несколько строк. Зто строковеє значение начинается на следующей строке программы и продолжается до строки, содержащей конечную лексему, которая должна стоять в самом начале зтой строки; ничего другого в зтой строке быть не должно. Неге-документы особенно полезны для создания HTML-документов.

Первая часть зтого строкового значення — определенно самая важная:

строка Content-Type задает тип генерируемой выходной информации. Сразу за ней идет пустая строка, которая не должна содержать пробелов и знаков табуляции.

У большинства новичков первые CGI-программы отказываются рабо-тать, потому что пользователи забывают об зтой пустой строке, отделяющей заголовок (нечто вроде заголовка сообщения злектронной почты) от следую-щего за ним необязательного тела*. После пустой строки следует HTML-документ, посылаемый в броузер пользователя, где он форматируется и ото-бражается.

Сначала добейтесь, чтобы ваша программа правильно вьшолнялась при вызове ее из командной строки. Зто необходимый, но не достаточный шаг для того, чтобы обеспечить функционирование вашей программы как сценария, работающего на сервере. Ошибки могут возникать и в других местах программы;

см. ниже раздел "Поиск и устранение ошибок в CGI-программах".

Если программа должным образом работает при вызове ее из командной строки, необходимо инсталлировать ее на компьютере-сервере. Приемлемые места размещения зависят от сервера, хотя для CGI-сценариев часто исполь-зуется каталог /usr/etc/httpd/cgi-bin/ и его подкаталоги. Обсудите зтот вопрос с Web-мастером или системным администратором.

После завершення инсталляции вашей программы в CGI-каталоге ее можно выполнять, указывая броузеру ее путевое имя в составе URL. Напри-мер, если ваша программа называется howdy, URL будет выглядеть так:

http://vww.SOMEWHERE.org/cgi-bin/howdy.

Сервери обычно позволяют использовать вместо ддинных путевых имен псевдонимы. Сервер, имеющий адрес www.SOMEWHERE.org, может запросто перевести cgi-bin/howdy, содержащийся в зтом URL, в нечто вроде usr/etc/httpd/ cgi-bin/howdy. Ваш системний администратор или Web-мастер может подска-зать, какой псевдоним следует использовать при обращении к вашей программе.

* Зтот заголовок необходим для протокола HTTP, о котором мы упоминали выше.



Простой клиент



Простой клиент

Для нашего простейшего клиента мы выберем довольно скучный сервис, который называется daytime ("время суток"). Сервер времени суток посылает установившему соединение клиенту одну строку данных, содержащую значение времени суток на этом удаленном сервере, а затем закрывает соединение.

Вот клиент:

#!/usr/bin/peri -w use 10::Socket;

$remote = 10::Socket::INET->new(

Proto => "tcp",

PeerAddr => "localhost",

PeerPort => "daytime(13)",

) or die "cannot connect to daytime port at localhost";

while ( <$remote> ) ( print )

Запустив эту программу, вы должны получить с сервера примерно следующее:

Thu May 8 11:57:15 1997

* IO::Socket входит в состав стандартного дистрибутива Perl версии 5.004. Если у вас более ранняя версия, получите этот модуль из CPAN, где вы найдете модули с простыми интерфейсами к следующим сервисам: DNS, ftp, Ident(RFC 931), NIS и NISPlus, NNTP, ping, POP3, SMTP, SNMP, SSLeay, telnet, time и др.

Вот что означают параметры конструктора new:

Proto

Протокол, который следует использовать. В данном случае возвращенный дескриптор гнезда будет подключен к TCP-гнезду, потому что нам нужно потоковое соединение, т.е. соединение, которое работает почти так же, как старый добрый файл. Не все гнезда такие. Например, с помощью протокола UDP можно создать дейтаграммное гнездо, используемое для передачи сообщений.

PeerAddr

Имя или Internet-адрес удаленного хоста, на котором работает сервер. Мы могли бы указать имя подлиннее, например www.perl.com, или адрес вроде 204.148.40.9. Однако для демонстрационных целей мы использовали специальное хост-имя localhost, которое всегда должно обозначать машину, на которой вы работаете. Имени localhost соответствует Internet-адрес 727.0.0.1.

PeerPort

Имя или номер порта сервиса, с которым мы хотим соединиться. В системах с нормально конфигурированным системным файлом серви-сов* мы могли бы обойтись просто именем daytime, но на всякий случай мы все же дали в круглых скобках номер порта (13). Использование одного номера без имени тоже сработало бы, но осторожные программисты стараются не использовать числа вместо констант.

Вы обратили внимание на то, как возвращаемое значение конструктора new используется в роли дескриптора файла в цикле while? Это то, что называется косвенным дескриптором файла — скалярная переменная, содержащая дескриптор файла. Его можно использовать точно так же, как обычный дескриптор. Например, так из него можно прочитать одну строку:

$line = <$handle>;

так — все остальные строки:

@lines = <$handle>;

а так — послать в него строку данных:

print $handle "some data\n";

* Системный файл сервисов в UNIX находится в каталоге /etc/services.



Работа с пользовательскими базами данных



Работа с пользовательскими базами данных









Развертывание



Развертывание

Если в качестве аргумента в командной строке стоит звездочка (*), то shell (или тот интерпретатор командной строки, которым вы пользуетесь) интерпретирует ее как список имен всех файлов, находящихся в текущем каталоге. Так, если вы дали команду rm *, то она удалит все файлы из текущего каталога. (Не делайте этого, если не хотите потом долго приставать к системному администратору с просьбами о восстановлении файлов.) Еще примеры: аргумент [а-т] *. с превращается в список имен файлов, находящихся в текущем каталоге, которые начинаются с одной из букв первой половины алфавита и заканчиваются на .с, а аргумент /etc/host* — в список имен файлов каталога /etc, которые начинаются со слова host. (Если для вас это ново, то перед дальнейшей работой рекомендуем почитать дополнительную литературу о сценариях shell.)

Преобразование аргументов вроде * или /etc/host* в список соответствующих имен файлов называется развертыванием (globbing). Perl обеспечивает развертывание с помощью очень простого механизма: подлежащий развертыванию образец заключается в угловые скобки или же используется функция glob.

@а = </etc/host*> @а = glob("/de/host*");

В списочном контексте, как показано выше, результатом развертывания является список всех имен, которые совпадают с образцом (как если бы shell раскрыл указанные аргументы), или пустой список, если совпадения не обнаружено. В скалярном контексте возвращается следующее совпадающее имя, а если совпадений нет — возвращается undef; это очень похоже на чтение из дескриптора файла. Например, просмотр имен по одному реализуется с помощью следующего кода:

while (defined($nextname = </etc/host*>)) ( print "one of the files is $nextname\n";

>

* Это справедливо для UNIX и большинства других современных операционных систем.

Здесь возвращенные имена файлов начинаются с префикса, соответствующего пути доступа к ним (/etc/host), поэтому, если вам нужна только последняя часть имени, придется выделять ее самостоятельно, например:

while ($nextname = </etc/host*>) (

$nextname =~ s#.*/##; # удалить часть до последней косой черты

print "one of the files is $nextname\n";

}

Внутри аргумента допускается указывать несколько образцов; эти списки развертываются отдельно, а затем конкатенируются так, как будто это один большой список:

@fred_barney_files = <fred* barney*>;

Другими словами, эта операция развертывания выдает те же значения, которые возвратила бы эквивалентная ей команда echo с такими же параметрами.*

Хотя развертывание списка файлов и сопоставление с регулярным выражением осуществляются почти одинаково, специальные символы имеют совершенно разный смысл. Не путайте эти механизмы, иначе будете удивляться, почему вдруг <\. с$> не находит все файлы, имена которых заканчиваются на .с!

В аргументе функции glob перед развертыванием производится интерполяция переменных. С помощью Perl-переменных можно выбирать файлы, задаваемые строкой, вычисляемой во время выполнения:

if (-d. "/usr/etc") (

$where = "/usr/etc";

} else (

$where = "/etc";

) Sfiles = <$where/*>;

Здесь переменной $ where присваивается одно из двух имен каталогов в зависимости от того, существует каталог /usr/etc или нет. Затем мы получаем список файлов, находящихся в выбранном каталоге. Обратите внимание:

переменная $where является развертываемой, т.е. подлежащими развертыванию символами являются /etc/* или /usr/etc/*.

Есть одно исключение из этого правила: образец <$var> (который означает использование в качестве развертываемого выражения переменной $var) должен записываться как <${var}>. В причины появления этого исключения сейчас лучше не вдаваться.**

* Для вас не будет сюрпризом узнать о том, что для выполнения развертывания Perl просто запускает C-shell, который раскрывает указанный список аргументов, и производит синтаксический анализ того, что получает обратно.

** Конструкция <$fred> читает строку из дескриптора файла, заданного содержимым скалярной переменной $ fred. Наряду с некоторыми другими особенностями, не упомянутыми в нашей книге, эта конструкция позволяет использовать косвенные дескрипторы файлов, где имя дескриптора передается и обрабатывается так, как будто это данные.



Регулярные выражения



Регулярные выражения









Сборник часто задаваемых вопросов


    Часто задаваемые вопросы (FAQ) по Perl — это собрание вопросов и ответов, которые часто появляются в телеконференции comp.lang.perl.misc. Во многих отношениях это собрание можно рассматривать как дополнение к имеющимся книгам. Здесь разъясняются понятия, в которых пользователи, возможно, не разобрались, и сообщается оперативная информация о таких вещах, как последняя редакция и лучший источник для получения исходного кода Perl.
    Этот сборник FAQ периодически публикуется в телеконференции comp.lang.perl.announce. Кроме того, его можно найти в Web по адресу http://www.perl.com/perl/faq.
    Начиная с версии Perl 5.004, этот FAQ включен в документацию стан-дартного дистрибутива. Вот его основные разделы, каждый из которых оформлен как отдельная man-страница:

perlfaq
Структурный обзор FAQ.
perlfaq1
Очень общая, высокоуровневая информация о языке Perl.
perlfaq2
Где найти исходный код и документацию на Perl, вопросы поддержки, обучения и сопутствующие вопросы.
perlfaq3
Инструментарий программиста
perlfaq4
Обработка чисел, дат, строк, массивов, хешей и разнообразные аспекты обработки данных.
perlfaq5
Ввод-вывод, дескрипторы файлов, запись на диск, форматы, нижние колонтитулы.
perlfaq6
Сопоставление с образцами и регулярные выражения.
perlfaq7
Общие вопросы, которые нельзя отнести ни к одной из других категорий.
perlfaq8
Межпроцессное взаимодействие, управление пользовательским интер-фейсом: клавиатура, экран, координатно-указательные устройства.
perlfaq9
Сети, Internet и кое-что о Web.



Сетевые клиенты



Сетевые клиенты

Немногие компьютеры (и, соответственно, работающие на них пользователи) остаются в изоляции от остального компьютерного мира. Сети, когда-то бывшие достоянием в основном государственных научно-исследовательских лабораторий и факультетов вычислительной техники крупнейших университетов, сейчас доступны практически каждому — даже пользователю домашнего компьютера с модемом, устанавливающему по коммутируемой линии соединение с провайдером с помощью протоколов SLIP или РРР. Сейчас сети больше чем когда-либо используются в повседневной работе организациями и пользователями всех слоев общества — для обмена электронной почтой, планирования встреч, управления распределенными базами данных, доступа к информации предприятий, получения прогнозов погоды, чтения текущих новостей, разговоров с собеседниками из другого полушария, рекламирования продуктов и фирм через Web и т.д.

У всех этих приложений есть одна общая черта: они работают на основе TCP, фундаментального протокола, который обеспечивает взаимодействие между собой всех сетей, входящих в Internet*. И мы имеем в виду не только Internet. He считая брандмауэров, базовая технология везде одна, независимо от того, устанавливается соединение по Internet, соединение между офисами компании или соединение между кухней и подвалом вашего дома. Это удобно: для того чтобы ориентироваться во всех применениях Internet/intra-net, вы должны изучить только одну технологию.

Как же с помощью сети позволить приложению, работающему на одной машине, общаться с другим приложением, которое может функционировать на совершенно другой машине? Средствами Perl это сделать очень легко, но сначала вам, наверное, нужно немного узнать о том, как работает сеть на базе протокола TCP.

* На самом деле коммуникации в Internet обеспечиваются протоколом IP (Internet Protocol), а протокол TCP (Transmition Control Protocol) является протоколом более высокого уровня.

Даже если вы еще ни разу не работали в компьютерной сети, вы уже знаете о системе с установлением соединений: это — телефонная сеть. И пусть вас не смущают причудливые словосочетания вроде "программирование систем клиент/сервер". Видя слово "клиент", читайте "вызывающий абонент", а видя слово "сервер" — читайте "отвечающий абонент".

Звоня кому-то по телефону, вы выступаете в роли клиента. Тот, кто поднимает трубку на другом конце линии, является сервером.

Программисты, имеющие опыт работы на С, возможно, знакомы с гнездами (sockets) . Гнездо — это интерфейс к сети в том же самом смысле, что и дескриптор файла — это интерфейс к файлам в файловой системе. В частности, для тех простых программ, которые мы продемонстрируем ниже, вы можете пользоваться дескриптором гнезда так же, как дескриптором файла*.

Вы можете читать данные из гнезда, записывать в него данные, а также выполнять обе эти операции. Это объясняется тем, что гнездо — особый вид двунаправленного дескриптора файла, представляющего сетевое соединение. В отличие от обычных файлов, созданных посредством функции open, гнезда создаются с помощью низкоуровневой функции socket.

Давайте выжмем еще немного из нашей телефонной модели. Звоня на коммутатор большой компании, вы можете попросить соединить вас с конкретным отделом по названию (например, с отделом кадров) или по номеру (например, "дополнительный 213"). Представьте, что каждый сервис, работающий на компьютере,— это отдел большой корпорации. Иногда сервис имеет несколько имен, например http и www, но только один номер, например 80. Этот номер, связанный с именем сервиса, называется его портом. С помощью Perl-функций getservbyname иgetservbyport МОЖНО найти имя сервиса по номеру его порта и наоборот. Вот некоторые стандартные ТСР-сервисы и номера их портов:

Сервис Порт Назначение
echo 7 Принимает все вводимые данные и воспроизводит их
discard 9 Принимает все, но ничего не возвращает
daytime 13 Возвращает текущую дату и местное время
ftp 21 Сервер для обработки запросов пересылки файлов
telnet 23 Сервер для интерактивных telnet-сеансов
smtp 25 Простой протокол пересылки почты; демон-почтальон
time 37 Возвращает число секунд, прошедших с начала 1900-го года (в двоичном формате)
http 80 Сервер World Wide Web
nntp 119 Сервер телеконференций

* Почти так же; поиск по гнезду невозможен.

Хотя гнезда изначально разрабатывались для Berkeley UNIX, все возрастающая популярность Internet побудила практически всех поставщиков операционных систем включить в свои продукты поддержку гнезд для программирования систем клиент/сервер. Функция socket является относительно низкоуровневой, а мы в нашей книге рассматриваем в основном высокоуровневые средства Perl. Рекомендуем вам пользоваться более удобным модулем IO::Socket*, который мы будем применять во всех наших примерах. Это значит, что мы также будем применять некоторые объектно-ориентированные конструкции Perl. Краткое введение в эти конструкции дано в главе 19. Более подробное введение в объектно-ориентированное программирование на Perl приведено на man-странице perltoot(l) и в главе 5 книги Programming Perl.

Подробное рассмотрение TCP/IP выходит за рамки нашей книги, но мы можем представить хотя бы несколько простых клиентов. О серверах, которые более сложны, вы можете узнать в главе 6 книги Programming Perl и на man-странице perlipc(l).



СGІпрограммирование



СGІ-программирование

Если в течение последних нескольких лет вы не сидели взаперти в деревянной хижине без злектричества, то вы наверняка слышали о World Wide Web. Web-адреса (больше известные как URL) сейчас можно найти везде: на рекламних плакатах и в титрах кинофильмов, на обложках журна-лов и на страницах других изданий, от газет до правительственных отчетов.

Многие из самых интересных Web-страниц включают разного рода формы, предназначенные для ввода данных пользователем. Вы вводите данные в такую форму и щелкаете на кнопке или рисунке. Зто действие запускает некую программу на Web-сервере, которая изучает введенные вами данные и генерирует новую выходную информацию. Иногда зта программа (широко известная как программа общего шлюзового интерфейса, или CGI-программа) представляет собой просто интерфейс к существующей базе данных; она преобразует введенные вами данные в нечто понятное для зтой базы данных, а выходную информацию базы данных — в нечто понятное для Web-броузера (обычно в HTML-форме).

CGI-программы не просто обрабатывают данные, введенные в форму. Они вызываются и тогда, когда вы щелкаете на графическом изображении, и фактически могут использоваться для отображения всего того, что "видит" ваш броузер. Web-страницы с CGI-поддержкой — зто не безликие и нудньге документы, а удивительно живые страницы с динамически изменяющимся содержимым. Именно динамическая информация делает Web интересньш и интерактивным источником информации, а не просто средством, предна-значенньш для чтения книги с терминала.

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

В зтой главе мы не только изучим основы CGI-программирования, но и параллельно получим определенные начальные сведения о гипертекстовых ссылках, библиотечных модулях и обьектно-ориентированном программи-ровании на Perl. В конце главы мы дадим начальные сведения о применении Perl для других видов Web-программирования.

Если рассматривать зту главу как отдельное пособие, следует отметить, что ее (как и любого другого документа обьемом меньше пары сотен страниц) недостаточно для изучения более сложных тем, затронутых здесь, в частности обьектно-ориентированного программирования и методов использования различных типов ссылок и запросов в WWW. Однако, будучи средством всего лишь предварительного ознакомления с тем, что вас ждет, представленные здесь примеры и пояснення к ним, возможно, побудят вас подробнее ознакомиться с затронутыми темами и дадут вам ориентиры для выбора соответствующих учебников. А если вы любите учиться на практико, они помогут вам сразу написать некоторые полезные программы на основе тех моделей, которые здесь представлены.

Мы предполагаем, что вы уже в основном знакомы с HTML.









Скалярные данные Что такое скалярные данные



Что такое скалярные данные

Скаляр — это простейший вид данных, которыми манипулирует Perl. Скаляр — это либо число (допустим, 4 или 3.25е20), либо строка символов (например, hello или Gettysburg Address). Хотя в общем-то числа и строки — это совершенно разные вещи, в Perl они используются практически как взаимозаменяемые понятия, поэтому мы опишем их в одной главе.

Над скалярной величиной можно производить операции (например, суммирование или конкатенацию), полученный результат, как правило, также является скаляром. Скалярную величину можно сохранять в скалярной переменной. Скаляры можно читать из файлов и с устройств, а также записывать в файлы и на устройства.

Числа

Хотя скаляр — это либо число, либо строка*, в данный момент нам будет полезно рассмотреть их отдельно. Итак, сначала числа, а через минуту — строки.

В Perl для всех чисел используется один и тот же внутренний формат

Как станет ясно из нескольких следующих абзацев, можно задавать и целые (чисто числовые значения, например 17 или 342), и числа с плавающей запятой (действительные числа, например 3,14 или 1,35, умноженное на 1025). При этом

* Или ссылка, но это более сложная тема.

во внутренних вычислениях Perl использует только значения с плавающей запятой двойной точности*. Это значит, что внутренних целых величин в Perl нет; целочисленная константа в программе рассматривается как эквивалентное значение с плавающей запятой**. Вы, вероятно, не заметите этого преобразования (или попросту проигнорируете его), но в любом случае не нужно искать целочисленные операции (как дополнение к операциям с плавающей запятой), ибо их попросту нет.

Литералы с плавающей запятой

Литерал — это способ представления величины в тексте Perl-программы. В своей программе вы можете называть литерал константой, но мы будем пользоваться термином литерал. Литералы — это способ представления данных в исходном тексте вашей программы как входной информации для компилятора Perl. (Данные, которые читаются из файлов или записываются в файлы, трактуются похоже, но не совсем так.)

Perl принимает полный набор литералов с плавающей запятой, которыми пользуются программисты, работающие на С. Допускаются числа с десятичными запятыми и без них (включая необязательный префикс "плюс" или "минус"). Кроме того, можно использовать показатель степени числа десять (экспоненциальное представление) с буквой Е. Например:

1.25 # около единицы с четвертью 7.25е45 # 7,25, умноженное на 10 в 45-й степени (большое число)

-6.5е24 # минус 6,5, умноженное на 10 в 24-й степени

# ("большое" отрицательное число)

-12е-24 # минус 12, умноженное на 10 в минус 24-й степени

# (очень маленькое отрицательное число)

-1.2Е-23 # еще одна форма записи этого числа

Целочисленные литералы

Целочисленные литералы также весьма просты, например:

12 15

-2004 3485

Не начинайте целое число с нуля, потому что Perl поддерживает восьмеричные и шестнадцатеричные литералы. Восьмеричные числа начинаются с нуля, а шестнадцатеричные — с Ох или ох***. Шестнадцатеричные цифры

- Значение с плавающей запятой двойной точности — это то, что компилятор С, который компилировал Perl, использовал для объявления double.

-* Если только вы не используете "целочисленный режим", но по умолчанию он не включен.

-** "Начальный нуль" работает только в литералах, но не действует при автоматическом преобразовании строк в числа. Строку данных, выглядящую как восьмеричное или шест-надцатеричное значение, можно преобразовать в число с помощью функций oct или hex.

от А до F (в любом регистре) обозначают обычные цифровые значения от 10 до 15. Например:

0377 t восьмеричное 377, то же самое, что десятичное 255

-Oxff # отрицательное шестнадцатеричное FF, то же самое, что десятичное -255

Строки

Строки — это последовательности символов (например, hello). Каждый символ представляет собой 8-битовое значение из 256-символьного набора (при этом символ NUL ничего особенного, как в некоторых языках, собой не представляет).

Самая короткая из возможных строк не содержит ни одного символа. Самая длинная строка заполняет всю наличную память (но сделать с ней что-либо вы вряд ли сможете). Это соответствует принципу "отсутствия встроенных ограничений", которому Perl следует при каждой возможности. Обычно строки представляют собой последовательности букв, цифр и знаков препинания, коды которых лежат в диапазоне ASCII 32 — 126. Однако возможность наличия в строке любого символа с кодом от 0 до 255 означает, что вы можете создавать, просматривать необработанные двоичные данные и манипулировать ими как строками — то, что вызвало бы серьезные трудности в большинстве других языков. (Например, можно "залатать" свою операционную систему, прочитав нужный фрагмент кода как Perl-строку, внеся изменение и записав результат обратно на диск.)

Как и числа, строки могут иметь литеральное представление (способ представления строкового значения в Perl-программе). Литеральные строки бывают двух видов: в одинарных кавычках и в двойных кавычках*. Есть еще одна похожая форма: строка в обратных кавычках ('вот такая'). Это не столько литеральная строка, сколько способ выполнения внешних команд и получения их результатов. Более подробно этот вопрос рассматривается в главе 14.

Строки в одинарных кавычках

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

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

* Есть также here -строки, похожие на here-локу менты shell. Они рассматриваются в главе 19, Программирование CGI. См. также главу 2 книги Programming РеНи man-страницу pcrldata( I).

в строку в одинарных кавычках обратную косую, поставьте перед ней еще одну обратную косую черту. Примеры:

'hello' # пять символов: h, e, 1, 1, о

'don\'t' # пять символов: d, о, п, одинарная кавычка, t

'' # пустая строка (без символов)

'silly\\me' # silly, обратная косая, те

'hello\n' # hello, обратная косая, п

'hello

there' # hello, новая строка, there (всего 11 символов)

Отметим, что пара символов \п внутри строки в одинарных кавычках интерпретируется не как символ новой строки, а как два символа: обратная косая и п. (Обратная косая имеет специальное значение только в том случае, если за ней следует еще одна обратная косая или одинарная кавычка.)

Строки в двойных кавычках

Строка в двойных кавычках действует почти так же, как С-строка. Это тоже последовательность символов, но на этот раз — заключенная в двойные кавычки. Теперь, однако, обратная косая приобретает полную силу и может задавать определенные управляющие символы и вообще любой символ в восьмеричном и шестнадцатеричном форматах. Вот некоторые строки в двойных кавычках:

"hello world\n" # hello world и новая строка

"new \177" # new, пробел и символ удаления (восьмеричное 177)

"coke\tsprite" # coke, знак табуляции, sprite

Широко используются сочетания обратной косой черты с различными символами (так называемая управляющая последовательность с обратной косой). Полный перечень управляющих последовательностей, которые применяются в строках в двойных кавычках, приведен в табл. 2.1.

Таблица 2.1. Управляющие последовательности

Конструкция Значение
\п Переход на новую строку (Newline)
Возврат к началу строки (Return)
\t Табуляция
\f Переход к новой странице (Formfeed)
\b Возврат на предыдущую позицию с удалением символа (Backspace)
Сигнал
Escape
Конструкция Значение
\007 Восьмеричное ASCII-значение (в данном случае 07 = сигнал)
\x7f Шестнадцатеричное ASCII-значение (в данном случае 7f = удалить)
\сС Управляющий символ (здесь Ctri+C)
\\ Обратная косая
\" Двойная кавычка
\1 Перевод следующей буквы в нижний регистр
\L Перевод в нижний регистр всех последующих букв до \е
\U Перевод в верхний регистр следующей буквы
\U Перевод в верхний регистр всех следующих букв до \Е
\Q Заключить в обратные косые все небуквенные и все нецифровые символы до \Е
\E Отменить действие последовательности \L, \u или \Q

Еще одна особенность строк в двойных кавычках состоит в том, что в них производится интерполяция переменных, т.е. при использовании строки все скалярные переменные и переменные-массивы в ней заменяются их текущими значениями. Мы, однако, формально еще не знакомы с тем, что такое переменная (за исключением краткого обзора, данного в главе 1), поэтому мы вернемся к этому вопросу позднее.



Скалярные операции



Скалярные операции

Оператор обозначает проведение определенной операции, благодаря которой создается новое значение (результат) из одного или нескольких других значений (операндов). Например, символ + обозначает операцию, потому что при его использовании берутся два числа (операнда, например, 5 и 6) и создается новое значение (11, результат).

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

Операции над числами

В Perl применяются обычные операции сложения, вычитания, умножения, деления и т.д. Например:

2+3 #2 плюс 3, или 5 5.1-2.4 #5,1 минус 2,4, или приблизительно 2,7 3 * 12 #3 умножить на 12 = 36 14/2 #14 делить на 2, или 7 10.2/0.3 # 10,2 делить на 0,3, или приблизительно 34 10/3 # всегда означает деление чисел с плавающей запятой, # поэтому результат приблизительно равен 3,3333333...

Кроме того, в Perl используется ФОРТРАН-подобная операция возведения в степень, по которой многие тоскуют в Паскале и С. Эта операция обозначается двумя звездочками, например 2**3 равно двум в степени три, или восьми. (Если этот результат "не помещается" в число с плавающей запятой двойной точности, например, отрицательное число возводится в нецелую степень или большое число возводится в большую степень, выдается сообщение об ошибке — fatal error)

Perl поддерживает также операцию деления с остатком. Значение выражения 10 % 3 — остаток от деления 10 на 3, или 1. Оба значения сначала сокращаются до целых, т.е. 10.5 % 3.2 вычисляется как 10 % 3.

Операции логического сравнения следующие: <, <=, ===, >==, >, !=. Эти операции сравнивают два значения в числовом формате и возвращают значение "истина" (true) или "ложь" (false). Например, операция 3 > 2 возвращает значение "истина", потому что три больше, чем два, тогда как операция 5 != 5 возвращает "ложь", потому что утверждение, что пять не равно пяти — неправда. Определение значений "истина" и "ложь" рассматривается позже, а пока можно считать, что "истина" — это единица, а "ложь" — нуль. (Вы еще увидите эти операции в табл. 2.2.)

Вас, вероятно, удивило слово "приблизительно" в комментариях к примерам, которые мы привели в начале этого раздела. Разве при вычитании 2, 4 из 5,1 не получается точно 2, 7? На уроке математики, может быть, и получается, но в компьютерах, как правило, — нет. В вычислительной технике получается приближенное значение, которое точно лишь до определенного числа десятичных разрядов. Числа в компьютерах хранятся не так, как их представляет себе математик. Впрочем, если вы не делаете чего-нибудь сверхсложного, то, как правило, увидите именно те результаты, которых ожидаете.

Сравнивая приведенные ниже операторы, вы увидите, что в действительности компьютер получил в результате вышеупомянутого вычитания (функция print f описывается в главе б):

printf ("%.51\n", 5.1 - 2.4)

# 2.699999999999999733546474089962430298328399658203125

print(5.1 - 2.4, "\n") ;

# 2.7

Не обращайте на это особого внимания: стандартный формат функции print для вывода на экран чисел с плавающей запятой обычно скрывает такие незначительные неточности представления. Если это создает какую-то проблему, следует воспользоваться объектными модулями Math::BigInt и Math::BigFloat — в них реализована арифметика с бесконечной точностью для целых и чисел с плавающей запятой, только выполняются эти операции несколько медленнее. Подробности вы найдете в главе 7 книги Programming Perl и в интерактивной (сетевой) документации на эти модули.

Операции над строками

Строковые значения можно конкатенировать операцией ".". (Да, это именно одна точка.) Данная операция изменяет строки-операнды не более, чем операция 2+з изменяет 2 или 3. Строку-результат (более длинную) можно использовать в дальнейших вычислениях или сохранить в переменной.

"hello" . "world" # то же самое, что "helloworld" •hello world' . "\n" # то же самое, что "hello world\n" "fred" . " " . "barney" # то же самое, что "fred barney"

Отметим, что конкатенацию нужно задавать явно при помощи знака ".", а не просто располагать два значения рядом друг с другом.

Еще одна группа операций над строками -— операции сравнения. Эти операции похожи на соответствующие операции ФОРТРАНа, например it (меньше чем) и т.д. Операции сравнения строк сравнивают ASCII-значения символов строк обычным способом. Полный набор операций сравнения (как для чисел, так и для строк) приведен в таблице 2.2.

Таблица 2.2. Операции сравнения чисел и строк

Сравнение Числовое Строковое
Равно == eq
Не равно ! = пе
Меньше чем < It
Больше чем > gt
Меньше чем или равно <= le
Больше чем или равно >== ge

Вы, возможно, спросите, почему предусмотрены отдельные операции для чисел и строк, если числа можно автоматически преобразовывать в строки и наоборот. Давайте рассмотрим два значения, 7 и 30. Если их сравнить как числа, то 7, очевидно, меньше 30. Если же сравнить их как строки, то строка "30" идет перед строкой "7" (ведь ASCII-значение цифры 3 меньше ASCII-значения цифры 7), поэтому 30 меньше 7. Perl всегда требует указывать тип сравнения, то есть конкретизировать, какое именно сравнение, числовое или строковое, будет проводиться.

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

Есть еще одна строковая операция — операция повторения строки, знак которой — строчная буква х. В результате выполнения этой операции возвращается столько конкатенированных копий левого операнда (строки), сколько указано в правом операнде (числе). Например:

"fred" х 3 # "fredfredfred" "barney" х (4+1) # "barney" x 5, или

# "barneybarneybarneybarneybarney" (3+2) х 4 #5х4, или "5" х 4, т.е. "5555"

Последний пример стоит объяснить подробнее. Круглые скобки указывают на то, что эту часть выражения нужно вычислить первой. (Круглые скобки здесь работают так же, как в обычной математике.) Однако в операции повторения строки левый операнд должен быть строкой, поэтому число 5 преобразуется в односимвольную строку "5" (правила этого преобразования мы рассмотрим позднее). Эта строка копируется четыре раза, что в итоге дает четырехсимвольную строку 5555. Если бы мы поменяли операнды местами, то у нас было бы пять копий строки 4, т.е. 44444. Это показывает, что повторение строки — некоммутативная операция.

При необходимости число копий (правый операнд) сначала усекается до целого значения (4, 8 превращается в 4). Если указать число копий, меньшее единицы, в результате выполнения операции получится пустая строка (строка нулевой длины).

Приоритет и ассоциативность операций

Разным операциям присваивается разный приоритет Это позволяет избежать неоднозначности такого рода, когда две операции пытаются манипулировать тремя операндами. Например, что выполнять в первую очередь в выражении 2+3*4 — сложение или умножение? Если сначала выполнить сложение, мы получим 5*4, или 20. Если же сначала выполнить умножение (как нас учили на уроках математики), то мы получим 2+12, или 14. К счастью, в Perl выбран стандартный математический вариант, т.е. умножение выполняется первым. Поэтому мы говорим, что умножение имеет более высокий приоритет, чем сложение.

Порядок выполнения операций, определяемый приоритетом, можно изменить с помощью круглых скобок. Выражение, заключенное в скобки, вычисляется в первую очередь, и лишь затем выполняется операция, стоящая за скобками (так, как нас учили на уроках математики). Следовательно, если бы нам захотелось выполнить сложение до умножения, необходимо было бы записать (2+3)*4, что дало бы в результате 20. Если вы хотите напомнить, что умножение выполняется перед сложением, можете добавить "декоративные", функционально бесполезные круглые скобки: 2+ (3*4).

При выполнении операций сложения и умножения* очередность их выполнения определяется достаточно просто, но, скажем, при конкатенации строк в сочетании с возведением в степень могут возникнуть проблемы. Единственно верный путь разрешить их —- обратиться к официальной, не предполагающей никаких исключений, таблице приоритетов Perl-операций. (Это табл. 2.3. Отметим, что некоторые из операций нами еще не описаны;

более того, они могут так и не появиться на страницах этой книги. Пусть, однако, этот факт не отпугнет вас.) Операции, аналогичные операциям С, имеют тот же приоритет, что и в этом языке.

Таблица 2.3. Ассоциативность и приоритет операций: от высокого к низкому

Ассоциативность Операция
Слева Операции над списками (справа налево)
Слева -> (вызов метода, разыменование)
Неассоциативные ++ ~ (автоинкремент, автодекремент)
Справа ** (возведение в степень)
Справа ! ~ \ + - (логическое отрицание, побитовое отри
цание, операция ссылки, унарное сложение, унарное вычитание)
Слева =~ !~ (совпадает, не совпадает)
Слева * / % х (умножение, деление, деление с остатком, повторение строки)
Слева + - . (сложение, вычитание, конкатенация строк)
Неассоциативные Именованные унарные операции (например, chomp)
Слева & (побитовое И)
Слева | А (побитовое ИЛИ, побитовое исключающее ИЛИ)
Слева &&

(логическое И)
Слева 1 | (логическое ИЛИ)
Неассоциативные . . ... (не включающие или включающие граничные значения диапазоны)
Справа ?: (операция выбора, if-then-else)
Справа = += -= *= и т.д. (присваивание и присваивание с вычислением)
Слева , => (запятая и запятая-стрелка)
Неассоциативные Операции над списками (слева направо)
Справа not (логическое НЕ)
Слева and (логическое И)
Слева or xor (логическое ИЛИ, логическое исключающее ИЛИ)

* Вы хорошо помните алгебру? Если нет, то в повсеместном использовании круглых скобок ничего предосудительного нет.

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

Операции одного уровня приоритетов разрешаются в соответствии с правилами ассоциативности. Как и приоритет, ассоциативность определяет порядок выполнения операций, если две операции с одинаковым приоритетом пытаются манипулировать тремя операндами:

2 ** з ** 4 # 2 ** (3 ** 4) , или 2 ** 81, или около 2.41е24 72/12/3 # (72 / 12) / 3, или 6/3, или 2 30 / 6 * 3 # (30/6) *3, или 15

В первом случае операция ** имеет ассоциативность справа, поэтому круглые скобки подразумеваются справа. Операции * и / имеют ассоциативность слева, поэтому круглые скобки подразумеваются слева.

Преобразование чисел в строки и обратно

Если строковое значение используется как операнд в операции с числами (например, в операции сложения), Perl автоматически преобразует эту строку в эквивалентное числовое значение, как если бы оно было введено в виде десятичного числа с плавающей запятой*. Нечисловые окончания и начальные пробельные символы игнорируются, поэтому " 123. 4 5 f red" (с начальным пробелом) без какого-либо предупреждения преобразуется в 123. 45**. Самый крайний из подобных случаев — когда нечто, не являющееся числом, вообще без предупреждения преобразуется в нуль (как строка fred, которую мы использовали здесь как число).

Если же, наоборот, там, где должно быть строковое значение, вводится числовое (например, в операции конкатенации строк), это числовое значение конвертируется в строку, которая была бы выведена на экран вместо него. Например, если вы хотите конкатенировать строку х с результатом умножения 4 на 5, это можно записать как

"X" .(4*5) f то же самое, что "X" . 20, или "Х20"

(Помните, что круглые скобки заставляют сначала выполнить 4*5, а затем выполнять операцию конкатенации.)

Другими словами, вам не нужно (в большинстве случаев) беспокоиться о том, что указано в качестве операнда — строка или число, поскольку Perl выполняет все необходимые преобразования самостоятельно.

* Шестнадцатеричные и восьмеричные значения этому автоматическому преобразованию не подлежат. Для интерпретации таких значений следует использовать функции hex и oct.

** Если в командной строке не указана опция -w. В целях обеспечения безопасности всегда задавайте эту опцию.



Скалярные операции и функции



Скалярные операции и функции

Самая распространенная операция, выполняемая над скалярной переменной — присваивание, которое представляет собой способ задания значения этой переменной. Операция присваивания в Perl обозначается знаком равенства (почти как в С и ФОРТРАНе). С левой стороны ставится имя переменной, с правой — присваиваемое ей значение или выражение, при помощи которого это значение вычисляется, например:

$а =17; # присвоить переменной $а значение 17

$b = $а + 3; # присвоить $Ь текущее значение $а плюс 3 (20)

$b == $b * 2; # присвоить $b значение $b, умноженное на 2 (40)

Обратите внимание: в последней строке переменная $Ь используется дважды: один раз для получения значения (в правой части), а второй — для указания, куда поместить вычисленное выражение (в левой части). Это допустимый, надежный и довольно распространенный прием. Распространен он настолько, что через минуту мы увидим, как все это можно записать в сокращенном виде.

Возможно, вы заметили, что скалярные переменные всегда предваряются знаком $. В shell знак $ используют для получения значения, а при присваивании нового значения его не указывают. В Java и С этот символ

* Их количество ограничено числом 255. Надеемся, этого достаточно. 2. Скалярные данные

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

Операции одного уровня приоритетов разрешаются в соответствии с правилами ассоциативности. Как и приоритет, ассоциативность определяет порядок выполнения операций, если две операции с одинаковым приоритетом пытаются манипулировать тремя операндами:

2 ** 3 ** 4 # 2 ** (3 ** 4) , или 2 ** 81, или около 2.41е24 72/12/3 # (72 / 12) / 3, или 6/3, или 2 30 / 6 * 3 # (30/6)*3, или 15

В первом случае операция ** имеет ассоциативность справа, поэтому круглые скобки подразумеваются справа. Операции * и / имеют ассоциативность слева, поэтому круглые скобки подразумеваются слева.

Преобразование чисел в строки и обратно

Если строковое значение используется как операнд в операции с числами (например, в операции сложения), Perl автоматически преобразует эту строку в эквивалентное числовое значение, как если бы оно было введено в виде десятичного числа с плавающей запятой*. Нечисловые окончания и начальные пробельные символы игнорируются, поэтому<< 12 3.4 5 f red" (с начальным пробелом) без какого-либо предупреждения преобразуется в 123. 45**. Самый крайний из подобных случаев — когда нечто, не являющееся числом, вообще без предупреждения преобразуется в нуль (как строка fred, которую мы использовали здесь как число).

Если же, наоборот, там, где должно быть строковое значение, вводится числовое (например, в операции конкатенации строк), это числовое значение конвертируется в строку, которая была бы выведена на экран вместо него. Например, если вы хотите конкатенировать строку х с результатом умножения 4 на 5, это можно записать как

"X" .(4*5) # то же самое, что "X" . 20, или "Х20"

(Помните, что круглые скобки заставляют сначала выполнить 4*5, а затем выполнять операцию конкатенации.)

Другими словами, вам не нужно (в большинстве случаев) беспокоиться о том, что указано в качестве операнда — строка или число, поскольку Perl выполняет все необходимые преобразования самостоятельно.

* Шестнадцатеричные и восьмеричные значения этому автоматическому преобразованию не подлежат. Для интерпретации таких значений следует использовать функции hex и oct.

** Если в командной строке не указана опция -w. В целях обеспечения безопасности всегда задавайте эту опцию.

Скалярные переменные

Имя переменной — это имя контейнера, который содержит одно или более значений. Имя переменной остается постоянным на протяжении всей программы, но значение (или значения), содержащееся в этой переменной, как правило, в ходе выполнения программы непрерывно изменяется.

Скалярная переменная содержит одно скалярное значение (представляющее собой число, строку или ссылку). Имена скалярных переменных состоят из знака доллара и буквы, за которой могут следовать еще несколько букв, цифр и знаков подчеркивания*. Строчные и прописные буквы различаются:

переменная $А и переменная $а — разные. Все буквы, цифры и знаки подчеркивания в имени переменной имеют значение. Так, переменная

$a_very_long_variable_that__end.s__in__l

отличается от переменной

$a_very_long_variable_that__ends_in_2

Имена переменных следует, как правило, подбирать так, чтобы они имели какой-то смысл в связи со значением переменной. Например, имя $xyzl23, вероятно, не очень описательно, в отличие от $line_length.

Скалярные операции и функции

Самая распространенная операция, выполняемая над скалярной переменной — присваивание, которое представляет собой способ задания значения этой переменной. Операция присваивания в Perl обозначается знаком равенства (почти как в С и ФОРТРАНе). С левой стороны ставится имя переменной, с правой — присваиваемое ей значение или выражение, при помощи которого это значение вычисляется, например:

$а =17; # присвоить переменной $а значение 17

$b = $а + 3; # присвоить $Ь текущее значение $а плюс 3 (20)

$b = $b * 2; # присвоить $b значение $b, умноженное на 2 (40)

Обратите внимание: в последней строке переменная $Ь используется дважды: один раз для получения значения (в правой части), а второй — для указания, куда поместить вычисленное выражение (в левой части). Это допустимый, надежный и довольно распространенный прием. Распространен он настолько, что через минуту мы увидим, как все это можно записать в сокращенном виде.

Возможно, вы заметили, что скалярные переменные всегда предваряются знаком $. В shell знак $ используют для получения значения, а при присваивании нового значения его не указывают. В Java и С этот символ

* Их количество ограничено числом 255. Надеемся, этого достаточно.

вообще опускается. Если вы постоянно присваиваете одной или нескольким переменным новые значения, то неминуемо будете делать ошибки. (Наше решение заключалось в том, чтобы прекратить писать программы на shell, awk и С, но для вас этот путь может быть неприемлем.)

Скалярное присваивание не только является операцией, его можно использовать и в качестве значения, аналогично тому как это делается в С. Другими словами, выражение $а=3 имеет значение, аналогично тому как имеет некоторое значение выражение $а+3. Значением является та величина, которая присваивается, т.е. $а=3 имеет значение 3. Хотя на первый взгляд это может показаться несуразным, использование присваивания как значения полезно, если вы хотите присвоить переменной промежуточное значение в выражении или просто скопировать одно значение в несколько переменных. Например:

$Ь = 4 + ($а =3); # присвоить 3 переменной $а, затем прибавить к 4,

# в результате чего $Ь получит значение 7 $d = ($с = 5); # скопировать 5 в $с, а затем и в $d $d = $с = 5; # то же самое, но без круглых скобок

Последнее выражение работает, потому что присваивание имеет ассоциативность справа.

Операции присваивания с вычислением

Выражения типа $ а == $а + 5 (где переменная стоит по обе стороны от оператора присваивания) встречаются довольно часто, поэтому в Perl применяется сокращенный метод записи операции изменения переменной — операция присваивания с вычислением. Почти все двоичные операции, с помощью которых вычисляются значения, имеют соответствующую форму с добавленным знаком равенства. Например, следующие две строки эквивалентны:

$а = $а + 5; # без операции присваивания с вычислением $а += 5; # с операцией присваивания с вычислением

Эквивалентны и эти строки:

$Ь = $Ь * 3;

$b *= 3;

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

Другой распространенной операцией присваивания является операция конкатенации строк:

$str = $str . " "; # добавить пробел к $str $str .= " "; # то же самое, но с операцией присваивания

Почти все двоичные операции, записанные таким образом, допустимы. Например, операция возведения в степень записывается как * * =. Так, $ а * * = 3 означает "возвести число, содержащееся в переменной $а, в третью степень и поместить результат обратно в $а".

Как и простая операция присваивания, эти операции также могут быть использованы в качестве значения, которым является новое значение переменной. Например:

$а = 3;

$b = ($а += 4); # $а и $b теперь равны 7

Автоинкремент и автодекремент

Казалось бы, для прибавления единицы к $а можно просто записать $а += 1, но Perl идет на шаг дальше и сокращает даже эту запись. Операция ++ (автоинкремент) прибавляет к своему операнду единицу и возвращает ин-крементированное значение, например:

$а += 1; # с операцией присваивания ++$а; # с префиксным автоинкрементом $d = 17;

$е = ++$d; # и $е, и $d теперь равны 18

Здесь операция ++ используется как префиксная, т.е. знак операции стоит слева от операнда. Автоинкремент можно записывать и в суффиксной форме (справа от операнда). В этом случае результатом выражения является старое значение переменной, которое она имела до инкрементирования. Например:

$с = 17;

$d = $с++; # $d равно 17, а $с равно 18

Поскольку значение операнда изменяется, операнд должен быть скалярной переменной, а не просто выражением. Не стоит рассчитывать, что ++16 получит значение 17, и нельзя сделать так, чтобы ++($а+$Ь) каким-то образом стало больше, чем сумма $а и $Ь.

Операция автодекремента (") похожа на операцию автоинкремента, но здесь не прибавляется единица, а вычитается. Как и операция автоинкремента, операция автодекремента имеет две формы — префиксную и суф-фиксную. Например:

$х = 12;

--$х; # $х теперь равно 11

$у = $х"; # $у равно 11, а $х - 10

Операции автоинкремента и автодекремента выполняются и над значениями с плавающей запятой. Автоинкрементирование переменной со значением 4,2 дает, как и следовало ожидать, 5,2*.

* Операция автоинкрементирования выполняется даже над строками. См. по данному вопросу книгу Programming Perl или man-страницу perlop(\).

Функции chop и chomp

Весьма полезной иногда бывает встроенная функция chop. Эта функция принимает один аргумент, указываемый в круглых скобках — имя скалярной переменной — и удаляет из строкового значения этой переменной последний символ. Например:

$х = "hello world";

chop($x); # $x теперь имеет значение "hello worl"

Обратите внимание: значение аргумента здесь меняется, отсюда и требование к наличию скалярной переменной, а не просто скалярного значения. Писать chop (' suey'), чтобы превратить аргумент в 'sue', не имеет смысла, потому что места для хранения этого значения нет. Кроме того, можно ведь и просто написать: ' sue '.

Возвращаемое значение для этой функции — отброшенный символ (в приведенном выше примере с "hello world" это буква d). Следующий код, очевидно, неверен:

$х == chop($x); # НЕВЕРНО: заменяет $х ее последним символом chop($x); # ВЕРНО: как и выше, удаляет последний символ

Если в функции chop задать пустую строку, то она ничего не сделает и ничего не возвратит, не выдавая сообщения об ошибке и вообще никак не реагируя*. Большинство операций в Perl имеют разумные граничные условия; другими словами, вы можете использовать их в пределах накладываемых ограничений и за ними, причем часто без какой-либо реакции с их стороны. Некоторые утверждают, что это один из фундаментальных недостатков Perl, а другие пишут первоклассные программы, нисколько не утруждая себя заботой о соблюдении этих ограничений. Вам решать, к какому лагерю присоединяться.

При усечении уже усеченной строки отбрасывается еще один символ. Например:

$а = "hello world\n";

chop $a; # теперь $а имеет значение "hello world" chop $a; # оп-ля! теперь $а имеет значение "hello worl"

Если вы не уверены в том, есть ли в конце переменной символ новой строки, можете использовать несколько более безопасную операцию chomp, которая удаляет только символ новой строки**, например:

$а == "hello world\n";

chomp ($a); # теперь $а имеет значение "hello world" chomp ($a); # ага! никаких изменений в $а не произошло

* Если вы не используете соответствующий здравому смыслу ключ -w.

** Или иное значение, которое задано переменной $ \ в качестве разделителя входных записей.

Интерполяция скаляров в строках

Если строковый литерал взят в двойные кавычки, в нем необходимо выполнить интерполяцию переменных (помимо проверки на наличие управляющих последовательностей с обратной косой). Это значит, что строка просматривается на предмет наличия имен скалярных переменных* — а именно комбинаций знака доллара с буквами, цифрами и знаками подчеркивания. Если ссылка на переменную имеется, она заменяется текущим значением этой переменной (или пустой строкой, если значение переменной еще не присвоено). Например:

$а = "fred";

$b = "some text $a"; # $b имеет значение "some text fred" $c = "no such variable $what"; # $c имеет значение "no such variable "

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

$х = *$fred'; # буквально знак доллара и слово "fred" $у = "hey $х"; # значение — 'hey $fred'; двойной подстановки нет

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

$fred = 'hi';

$barney = "a test of " . '$fred'; # буквально: 'a test of $fred' $barney2= "a test of \$fred"; # то же самое

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

$fred = "pay"; $fredday = "wrong!";

$barney = "It's $fredday"; # не день зарплаты payday, a "It's wrong!" $barney = "It's ${fred}day"; # теперь $barney получает значение "It's payday" $barney2 = "It's $fred"."day"; # еще один способ $barney3 = "It's " . $fred . "day"; # и еще один

* И переменных-массивов, но этот вопрос мы будем рассматривать в главе 3 Массивы и списочные данные.

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

$bigfred = "\Ufred"; # $bigfred имеет значение "FRED"

$fred = "fred"; $bigfred = "\U$fred"; # то же самое

$capfred = "\u$fred"; # $capfred имеет значение "Fred"

$barney = "\LBARNEY"; # $barney теперь имеет значение "barney"

$capbarney = "\u\LBARNEY"; # $capbarney теперь имеет значение "Barney"

$bigbarney = "BARNEY"; $capbarney = "\u\L$bigbarney"; # то же самое

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

Термин "интерполяция переменных" иногда заменяют термином "интерполяция двойных кавычек", потому что интерполяция выполняется в строках, заключенных в двойные кавычки. (А также в строках, заключенных в обратные кавычки, которые мы рассмотрим в главе 14.)



Скалярные переменные



Скалярные переменные

Имя переменной — это имя контейнера, который содержит одно или более значений. Имя переменной остается постоянным на протяжении всей программы, но значение (или значения), содержащееся в этой переменной, как правило, в ходе выполнения программы непрерывно изменяется.

Скалярная переменная содержит одно скалярное значение (представляющее собой число, строку или ссылку). Имена скалярных переменных состоят из знака доллара и буквы, за которой могут следовать еще несколько букв, цифр и знаков подчеркивания*. Строчные и прописные буквы различаются:

переменная $А и переменная $а — разные. Все буквы, цифры и знаки подчеркивания в имени переменной имеют значение. Так, переменная

$a_very_long_variable_that_ends_in_l

отличается от переменной

$a_very_long_variable_that_ends_in_2

Имена переменных следует, как правило, подбирать так, чтобы они имели какой-то смысл в связи со значением переменной. Например, имя $xyzl23, вероятно, не очень описательно, в отличие от $line_length.



Скалярный и списочный контексты


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

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









Сообщения о дефектах


В том невероятном случае, если вы наткнетесь на дефект не в вашей собственной программе, а в самом Perl, постарайтесь проверить его на мини-мальном по объему контрольном примере, а затем документировать с помощью программы perlbug, которая поставляется вместе с исходным кодом Perl.









Сортировка по заданным критериям


Чтобы задать порядок сортировки, следует определить программу срав-нения, которая задает способ сравнения двух злементов. Для чего она нужна? Нетрудно понять, что сортировка — зто размещение множества злементов в определенном порядке путем их сравнения между собой. Поскольку сравнить сразу все злементы нельзя, нужно сравнивать их по два й, используя результати зтих попарных сравнений, расставить все их по местам.

Программа сравнения определяется как обычная подпрограмма. Она будет вызываться многократно, и каждый раз ей будут передаваться два аргумента сортируемого списка. Данная подпрограмма должна определить, как первое значение соотносится со вторым (меньше, равно или больше), и возвратить закодированное значение (которое мы опишем чуть ниже). Зтот процесе повторяется до тех пор, пока не будет рассортирован весь список.

Чтобы повысить скорость вьшолнения, зти два значення передаются в подпрограмму не в массиве, а как значення глобальных переменных $а и $Ь. (Не волнуйтесь: исходные значення $ а и $Ь належно защищены.) Зта подпрограмма должна возвратить любое отрицательное число, если $а меньше $Ь, нуль, если $а равно $Ь, и любое положительное число, если $а больше $Ь. Теперь учтите, что "меньше чем" соответствует вашому пониманию зтого результата в данном конкретном случае; зто может бьггь сравнение чисел, сравнение по третьому символу строки, наконец, сравнение по значенням какого-то хеша с использованием передаваемьгх значений как ключей — в общем, зто очень гибкий механизм.

Вот пример подпрограммы сортировки в числовом порядке:

sub by_number (

if ($a < $b) ( return -1;

} elsif ($a == $b) ( return 0;

} elsif ($a > $b) ( return 1;

) >

Обратите внимание на имя by_number. На первый взгляд, в имени зтой подпрограммы нет ничего особенного, но скоро вы поймете, почему нам нравятся имена, которые начинаются с префикса Ьу_.

Давайте разберем зту подпрограмму. Если значение $а меньше (в данном случае в числовом смысле), чем значение $Ь, мы возвращаем значение -1.

Если значення численно равнн, мы возвращаем нуль, а в противном случае возвращаем 1. Таким образом, в соответствии с нашей спецификацией программы сравнения для сортировки зтот код должен работать.

Как использоватьданную программу? Давайте попробуєм рассортировать такой список:

Bsomelist = (1,2,4,8,16,32,64,128,256);

Если использовать с зтим списком обычную функцию sort без всяких "украшений", числа будут рассортированы так, как будто зто строки, причем сортировка будет выполнена с учетом кодов ASCII, т.е.:

Swronglist = sort Osomelist; # Owronglist теперь содержит (1,128,16,2,256,32,4,64,8)

Конечно, зто не совсем числовой порядок. Давайте используем в функ-ции sort нашу только что определенную программу сортировки. Имя зтой программы ставится сразу после ключового слова sort:

@rightlist = sort by_number Swronglist;

* @rightlist теперь содержит (1,2,4,8,16,32,64,128,256)

Задача решена. Обратите внимание: функцию sort можно прочитать вместе с ее спутницей, программой сортировки, на человеческом языке, т.е. "рассортировать по числовым значенням". Бот почому мы использовали в имени подпрограммы префикс Ьу_ ("по").

Такое тройное значение (-1, 0, +1), отражающее результаты сравнения числовьк значений, встречается в программах сортировки достаточно часто, позтому в Perl єсть специальная операция, которая позволяет сделать все зто за один раз. Зту операцию часто называют "челноком" (или "космическим кораблем", как следует из дословного перевода английского spaceship), потому что ее знак — <=>.

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

sub by_number ( $а <=> $b;

}

Обратите внимание на знак операции между двумя переменными. Да, он действительно состоит из трех символов. Зта операция возвращает те же значення, что и цепочка if/elsif из предыдущего определения зтой программы. Теперь все записано очень кратко, но зтот вызов можно сокра-тить и дальше, заменив имя подпрограммы сортировки самой подпрограм-мой, записанной в той же строке:

@rightlist " sort ( $а <=> $b } @wronglist;

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

Для операции сравнения числових значений "челнок" єсть соответст-вующая строковая операция — стр*. Зта операция возвращает одно из трех значений в зависимости от результата сравнения двух аргументов по строковим значенням. Вот как можно по-другому записать порядок сортировки, который установлен по умолчанию:

@result = sort ( $а cmp $b } Osomelist;

Вам, вероятно, никогда не придется писать именно такую подпрограмму (имитирующую встроенную функцию стандартной сортировки) — если только вы не пишете книгу о Perl. Тем не менее, операция стр все же находит применение в каскадних схемах упорядочивания. Например, вам необходи-мо расставить злементы по численним значенням, если они численно не равны; при равенстве они должны идти быть упорядочены по строковим значенням. (По умолчанию приведенная выше подпрограмма by_number просто ставит нечисловые строки в случайном порядке, потому что при сравнении двух нулевих значений числовое упорядочение провести нельзя.) Вот как можно сказать "числовые, если они численно не равны, иначе строковие":

sub by mostly_numeric (

($a <=> $b) I I ($a cmp $b) ;

)

Зтот код работает следующим образом. Если результат работы "челнока" равен -1 или 1, то остальная часть вираження пропускается и возвращается -1 или 1. Если "челнок" дает нуль, то вьшолняется операция cmp, которая возвращает соответствующее значение, сравнивая сортируемие значення как строки.

Сравниваются не обязательно те значення, которне передаются в про-грамму. Пусть, например, у вас єсть хеш, ключи которого — регистрацион-ние имена, а значення — реальнне имена пользователей. Предположим, ви хотите напечатать таблицу, в которой регистрационнне и реальные имена будут рассортированн по порядку реальних имен.

Сделать зто довольно легко. Давайте Предположим, что значення находятся в массиве % names. Регистрационнне имена, таким образом, представляют собой список keys (%names). Нам нужно получить список регистрационных имен, рассортированных по соответствующим значенням, позтому для любого конкретного ключа $а мы должны проверить значение $ names ($а} и провести

* Не вполне соответствующая. Встроенная функция sort отбрасывает злементы undef, а зта функция — нет.

сортировку относительно данного значення. Если следовать зтой логике, то программа практически напишется сама:

@sortedkeys = sort_by_name keys (%names);

sub by_names (

return $names{$a} cmp $names($b};

) foreach (@sortedkeys) {

print "$_ has a real name of $names($_)\n";

}

K зтому нужно еще добавить "аварийное" сравнение. Предположим, что реальные имена двух пользователей совпадают. Из-за капризной натуры программы sort мы в перами раз можем получить зти значення в одном порядке, а во второй раз — в другом. Зто плохо, если данный результат придется, например, вводить в программу сравнения для формирования отчета, позтому следует избегать таких вещей. Задача легко решается с помощью операции cmp:

sub by_names {

($names($a} cmp $names($b}) || ($a cmp $b);

Если реальные имена совпадают, то еортировка здесь производится на оснований регистрационного имени. Поскольку регистрационные имена уникальны (ведь, помимо всего прочего, они являются ключами хеша, а ключи совпадать не могут), мы можем добиться нужного нам результата. Если вы не хотите, чтобы поздно вечором раздался звонок от системного администратора, удивленно спрашивающего, почему включается аварийная сигнализация — лишите хорошие программы днем!



Создание CGIпрограммы гостевой


* Как мы отметим ниже, это приложение можно было бы назвать программой Webchat (переговоров через Web).

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

Вероятно, вы хотите, чтобы сообщения в гостевой книге сохранялись и по завершении посещения вашего узла тем или иным пользователем, поэтому вам нужен файл, куда они будут записываться. Гостевая CGI-программа (вероятно) работает не под вашим управлением, поэтому у нее, как правило, не будет права на обновление вашего файла. Следовательно, первое, что необходимо сделать,— это создать для нее файл с широкими правами доступа. Если вы работаете в UNIX-системе, то можете сделать (из своего shell) для инициализации файла программы гостевой книги следующее:

touch /usr/tmp/chatfile chmod 0666 /usr/tmp/chatfile

Отлично, но как обеспечить одновременную работу с программой гостевой книги нескольких пользователей? Операционная система не блокирует попытки одновременного доступа к файлам, поэтому если вы будете недостаточно осторожны, то получите файл, в который записывают сообщения все пользователи одновременно. Чтобы избежать этого, мы используем Perl-функцию flock, позволяющую пользователю получить монопольный доступ к файлу, который мы разрешаем обновить. Это будет выглядеть примерно так:

use Fcnti qw(:flock); # импортирует LOCK_EX, LOCKJ3H, LOCK_NB flock(CHANDLE, LOCK_EX) || bail ("cannot flock $CHATNAME: $!");

Аргумент lock_ex функции flock — вот что позволяет нам обеспечить монопольный доступ к файлу*.

Функция flock представляет собой простой, но универсальный механизм блокировки, несмотря на то, что его базовая реализация существенно изменяется от системы к системе. Она не возвращает управление до тех пор, пока файл не будет разблокирован. Отметим, что блокировки файлов носят чисто рекомендательный характер: они работают только тогда, когда все процессы, обращающиеся к файлу, соблюдают эти блокировки одинаково. Если три процесса соблюдают блокировки, а один не соблюдает, то не функционирует ни одна из блокировок.

* В версиях Perl до 5.004 вы должны превратить в комментарий use Fcnti и в качестве аргумента функции flock использовать просто 2.

Объектно-ориентированное программирование на Perl

Наконец пришло время научить вас пользоваться объектами и классами — и это важнее всего. Хотя решение задачи построения вашего собственного объектного модуля выходит за рамки данной книги, это еще не повод для того, чтобы вы не могли использовать существующие объектно-ориентированные библиотечные модули. Подробная информация об использовании и создании объектных модулей приведена в главе 5 книги Programming Perl и на man-странице perltoot(l).

Мы не будем углубляться здесь в теорию объектов, и вы можете просто считать их пакетами (чем они и являются!) удивительных, великолепных вещей, вызываемых косвенно. Объекты содержат подпрограммы, которые делают все, что вам нужно делать с объектами.

Пусть, например, модуль CGI.pm возвращает объект $query, который представляет собой входные данные пользователя. Если вы хотите получить параметр из этого запроса, вызовите подпрограмму par am () :

$query->param("answer");

Данная запись означает: "Выполнить подпрограмму param () с объектом $query, используя "answer" как аргумент". Такой вызов в точности соответствует вызову любой другой подпрограммы, за исключением того что вы используете имя объекта, за которым следует синтаксическая конструкция ->. Кстати, подпрограммы, связанные с объектами, называются методами.

Если вы хотите получить значение, возвращенное подпрограммой param (), воспользуйтесь обычным оператором присваивания и сохраните это значение в обычной переменной $he_said:

$he_said = $query->param("answer");

Объекты выглядят как скаляры; они хранятся в скалярных переменных (таких как переменная $ query в нашем примере), и из них можно составлять массивы и хеши. Тем не менее их не следует рассматривать как строки и числа. По сути дела, это особый вид ссылок, их нельзя рассматривать как обычные ссылки. Объекты следует трактовать как особый, определяемый пользователем тип данных.

Тип конкретного объекта известен как его класс. Имя класса обычно состоит из имени модуля без расширения рт, и к тому же термины "класс" и "модуль" часто используются как эквиваленты. Таким образом, мы можем говорить о CGI-модуле или о CGI-классе. Объекты конкретного класса создает и контролирует модуль, реализующий этот класс.

Доступ к классам осуществляется путем загрузки модуля, который выглядит точно так же, как любой другой модуль, за исключением того что объектно-ориентированные модули обычно ничего не экспортируют. Вы можете рассматривать класс как фабрику, которая производит совершенно новые объекты. Чтобы класс выдал один из таких объектов, нужно вызвать специальный метод, который называется конструктор.

Вот пример:

$query = CGI->new(); # вызвать метод new() в классе "CGI"

Здесь мы имеем дело с вызовом метода класса. Метод класса выглядит точно так же, как метод объекта (о котором мы говорили секунду назад), за исключением того что вместо использования объекта для вызова метода мы используем имя класса, как будто он сам — объект. Метод объекта говорит "вызвать функцию с этим именем, которая относится к данному объекту", тогда как метод класса говорит "вызвать функцию с этим именем, которая относится к данному классу".

Иногда то же самое записывается так:

$query = new CGI; # то же самое

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

С точки зрения разработчика объектных модулей, объект — это ссылка на определяемую пользователем структуру данных, часто на анонимный хеш. Внутри этой структуры хранится всевозможная интересная информация. Воспитанный пользователь, однако, должен добираться до этой информации (с целью ее изучения или изменения), не рассматривая объект как ссылку и не обращаясь непосредственно к данным, на которые он указывает, а используя только имеющиеся методы объектов и классов. Изменение данных объекта другими средствами — это нечестная игра, после которой о вас обязательно станут говорить и думать плохо. Чтобы узнать о том, что представляют собой и как работают вышеупомянутые методы, достаточно прочитать документацию на объектный модуль, которая обычно прилагается в pod-формате.

Объекты в модуле CGI.pm

CGI-модуль необычен в том смысле, что его можно рассматривать либо как традиционный модуль с экспортируемыми функциями, либо как объектный модуль. Некоторые программы пишутся гораздо легче с помощью объектного интерфейса к модулю CGI.pm, нежели с помощью процедурного интерфейса к данному модулю. Наша гостевая книга — одна из таких программ. Мы получаем доступ к входной информации, которую пользователь ввел в форму, через CGI-объект и можем, при желании, с помощью этого же объекта генерировать новый HTML-код для отправки обратно пользователю.

Сначала, однако, нам нужно создать этот объект явно. Для CGI.pm, как и для многих других классов, метод, который позволяет создавать объекты,— это метод класса new () *.

* В отличие от C++ Perl не считает new ключевым словом; вы совершенно свободно можете использовать такие методы-конструкторы, как gimme_another() или fred.0. Тем не менее большинство пользователей в итоге приходят к тому, что называют свои конструкторы во всех случаях new ().

Данный метод конструирует и возвращает новый CGI-объект, соответствующий заполненной форме. Этот объект содержит все данные, введенные пользователем в форму. Будучи вызванным без аргументов, метод new () создает объект путем чтения данных, переданных удаленным броузером. Если в качестве аргумента указан дескриптор файла, он читает этот дескриптор, надеясь найти в нем данные, введенные в форму в предыдущем сеансе работы с броузером.

Через минуту мы покажем вам эту программу и поясним ее работу. Давайте предположим, что она называется guestbook и находится в каталоге cgi-bin. Хоть она и не похожа ни на один из тех сценариев, которые мы рассмотрели выше (в которых одна часть выводит HTML-форму, а вторая читает данные, введенные в форму пользователем, и отвечает на них), вы увидите, что она, тем не менее, выполняет обе эти функции. Поэтому отдельный HTML-документ, содержащий форму гостевой книги, нам не нужен. Пользователь мог бы сначала запустить нашу программу, просто щелкнув мышкой на такой ссылке:

Please sign our <А HREF="http://www.SOMEWHERE.org/cgi-bin/guestbook">guestbook</A>.

Затем программа загружает в броузер HTML-форму и, на всякий случай, предыдущие сообщения гостей (в ограниченном количестве), чтобы пользователь мог их просмотреть. Пользователь заполняет форму, передает ее, и программа читает то, что передано. Эта информация добавляется в список предыдущих сообщений (хранящийся в файле), который затем вновь выводится в броузер, вместе со свежей формой. Пользователь может продолжать чтение текущего набора сообщений и передавать новые сообщения, заполняя предлагаемые формы, столько раз, сколько сочтет необходимым.

Вот наша программа. Перед тем как разбирать ее поэтапно, вы, возможно, захотите просмотреть программу целиком.

#!/usr/bin/peri -w

use 5.004;

use strict; # установить объявления и взятие в кавычки use CGI qw(:standard); # импортировать сокращения согласно :standard use Fcnti qw(:flock); # импортирует LOCK_EX, LOCKJ3H, LOCK_NB

sub bail ( # функция обработки ошибок

my $error = "@ ";

print hi("Unexpected Error"), p($error), end html;

die $error;

!

my(

$CHATNAME, # имя файла гостевой книги $MAXSAVE, # какое количество хранить $TITLE, # название и заголовок страницы @cur, # все текущие записи

Sentry, # одна конкретная запись ) ;

$TITLE = "Simple Guestbook";

$CHATNAME = "/usr/tmp/chatfile"; # где все это в системе находится $MAXSAVE =10;

print header, start_html($TITLE), hi ($TITLE);

$cur ” CGI->new(); # текущий запрос if ($cur->param("message")) ( # хорошо, мы получили сообщение

• $cur->param("date", scalar localtime); # установить текущее время Sentries = ($cur); # записать сообщение в массив }

# открыть файл для чтения и записи (с сохранением предыдущего содержимого) open(CHANDLE, "+< $CHATNAME") II bail("cannot open $CHATNAME: $!");

# получить эксклюзивную блокировку на гостевую книгу

# (LOCK_EX == exclusive lock)

flock(CHANDLE, LOCK_EX) || bail("cannot flock $CHATNAME: $!");

# занести в $MAXSAVE старые записи (первой — самую новую) while (!eof(CHANDLE) && Sentries < $MAXSAVE) (

$entry = CGI->new(\*CHANDLE); t передать дескриптор файла по ссылке

push Sentries, $entry;

}

seek(CHANDLE, 0, 0) 11 bail("cannot rewind $CHATNAME: $!");

foreach $entry (Sentries) (

$entry->save(\*CHANDLE); # передать дескриптор файла по ссылке } truncate(CHANDLE, tell(CHANDLE)) || bail("cannot truncate $CHATNAME: $!");

close(CHANDLE) || bail ("cannot close $CHATNAME: $!");

print hr, start form; # hr()проводит горизонтальную линию: <HR> print p("Name:", $cur->textfield(

-NAME => "name")) ;

print p("Message:" $cur->textfield(

-NAME => "message",

-OVERRIDE => 1, # стирает предыдущее сообщение

-SIZE => 50)) ;

print p(submit("send"), reset("clear"));

print end_form, hr;

print h2("Prior Messages");

foreach $entry (Sentries) f

printf("%s [%s]: %s",

$entry->param("date"),

$entry->param("name"),

$entry->param("message")) ;

print br() ;

} print end_html;

На Рисунок 19.5 вы видите изображение, которое появляется на экране после запуска этой программы.

Рисунок 19.5. Форма простой гостевой книги

Обратите внимание на то, что программа начинается с оператора

usa 5.004;

Если вы хотите запускать ее с помощью более ранние версии Perl 5, то нужно превратить в комментарий строку

use Fcnti qw(:flock)

и заменить lock_ex в первом вызове flock на z.

Поскольку каждое выполнение программы приводит к возврату HTML-формы в броузер, который обратился к программе, то программа начинается с задания HTML-кода:

print header, start_html($TITLE), hi($TITLE) ;

Затем создается новый CGI-объект:

$cur = CGI->new(); # текущий запрос

if ($cur->param("message")) ( # хорошо, мы получили сообщение

$cur->param("date", scalar localtime); # установить текущее время

Sentries = ($cur); # записать сообщение в массив

>

Если нас вызывают посредством заполнения и передачи формы, то объект $cur должен содержать информацию о тексте, введенном в форму. Форма, которую мы предлагаем (см. ниже), содержит два поля ввода: поле имени для ввода имени пользователя и поле сообщения для ввода сообщения. Кроме того, приведенный выше код ставит на введенные в форму данные (после их получения) метку даты. Передача в метод param() двух аргументов — это способ присваивания параметру, заданному первым аргументом, значения, указанного во втором аргументе.

Если нас вызывают не посредством передачи формы, а выполняя щелчок мышью на ссылке Please sign our guestbook, то объект запроса, который мы создаем, будет пуст. Проверка if даст значение "ложь", и в массив Sentries никакой элемент занесен не будет.

В любом случае мы переходим к проверке наличия записей, созданных ранее в нашем сохраняемом файле. Эти записи мы будем считывать в массив @entries. (Вспомните о том, что мы только что сделали текущие данные, если они имеются в форме, первым элементом этого массива.) Но сначала мы должны открыть сохраняемый файл:

open(CHANDLE, "+< $CHATNAME") || bail("cannot open $CHATNAME: $!");

Эта функция открывает файл в режиме неразрушающего чтения-записи. Вместо open можно использовать sysopen (). При таком способе посредством единственного вызова открывается старый файл, если он существует (без уничтожения его содержимого), а в противном случае создается новый файл:

# нужно импортировать две "константы" из модуля Fcnti для sysopen use Fcnti qw( 0_RDWR 0_CREAT );

sysopen(CHANDLE, $CHATFILE, 0_RDWRI0_CREAT, 0666) || bail "can't open $CHATFILE: $!";

Затем мы блокируем файл, как описывалось выше, и переходим к считыванию текущих записей из $ мах save в Sentries:

flock(CHANDLE, LOCK_EX) 11 bail("cannot flock $CHATNAME: $!");

while (!eof(CHANDLE) &S Sentries < $MAXSAVE) {

$entry = CGI ->new(\*CHANDLE); # передать дескриптор файла по ссылке

push Sentries, $entry;

}

Функция eof — встроенная Perl-функция, которая сообщает о достижении конца файла. Многократно передавая в метод new () ссылку на дескриптор сохраняемого файла*, мы выбираем старые записи, по одной при каждом вызове. Затем мы обновляем файл так, чтобы он включал новую запись, которую мы (возможно) только что получили:

seek(CHANDLE, 0, 0) || bail("cannot rewind $CHATNAME: $!");

foreach $entry (Sentries) {

$entry->save(\*CHANDLE); # передать дескриптор файла по ссылке } truncate(CHANDLE, tell (CHANDLE)) || bail("cannot truncate $CHATNAME: $!");

close (CHANDLE) || bailC'cannot close $CHATNAME: $!");

Функции seek, truncate и tell —встроенные Perl-функции, описания которых вы найдете в любом справочнике по языку Perl. Здесь seek переставляет указатель позиции в начало файла, truncate усекает указанный файл до заданной длины, a tell возвращает текущее смещение указателя позиции в файле по отношению к началу файла. Назначение этих строк программы — сохранить в файле только самые последние записи $maxsave, начиная с той, которая была сделана только что.

Метод save () обеспечивает собственно создание записей. Его можно вызвать здесь как $entry->save, поскольку $entry — это CGI-объект, созданный с помощью CGl->new ().

Формат записи сохраняемого файла выглядит следующим образом (запись завершается знаком "=", стоящим в отдельной строке):

ИМЯ1=ЗНАЧЕНИЕ1 ИМЯ2=ЗНАЧЕНИЕ2 ИМЯЗ=ЗНАЧЕНИЕЗ

Теперь пора возвратить обновленную форму броузеру и его пользователю. (Это будет, конечно, первая форма, которую он видит, в том случае, если он щелкнул на ссылке Please sign our guestbook.) Сначала некоторые предварительные действия:

print hr, start_form; # hr() проводит горизонтальную линию: <HR>

Как мы уже упоминали, CGI.pm позволяет нам использовать либо прямые вызовы функций, либо вызовы методов через CGI-объект. В нашей программе для создания базового HTML-кода мы обратились к простым вызовам функций, а для задания полей ввода формы продолжаем пользоваться методами объектов:

print pC'Name:", $cur->textfield( -NAME => "name")) ;

print p("Message:" $cur->textfield(

-NAME => "message",

-OVERRIDE => 1, # стирает предыдущее сообщение

-SIZE => 50)) ;

print p(submit("send"), reset("clear"));

print end_form, hr;

* Фактически она представляет собой glob-ссылку, а не ссылку на дескриптор файла, но в данном случае это почти то же самое.

Метод textfieid() возвращает поле ввода текста для нашей формы. Первый из двух приведенных выше вызовов генерирует HTML-код поля ввода текста с HTML-атрибутом, NAME="name", а второй — создает поле с атрибутом NAME="message" .

Компоненты формы, создаваемые модулем CGI.pm, по умолчанию устойчивы: они сохраняют свои значения до следующего вызова. (Но лишь в течение одного "сеанса" работы с формой, считая с того момента, когда пользователь щелкнул на ссылке Please sign our guestbook.) Это значит, что поле name = "name", созданное в результате первого вызова textfield(), будет содержать значение имени пользователя, если он уже хотя бы один раз в этом сеансе заполнял и передавал форму. Таким образом, поле ввода, которое мы сейчас создаем, будет иметь следующие HTML-атрибуты:

NAME="name" VALUE="Sam Smith"

Совсем другое дело — второй вызов text field (). Мы не хотим, чтобы поле сообщения содержало значение старого сообщения. Поэтому пара аргументов -override => 1 указывает: "Выбросить предыдущее значение этого текстового поля и восстановить значение по умолчанию". Пара аргументов -size => 50 задает размер (в символах) отображаемого поля ввода. Помимо показанных здесь, могут быть и другие необязательные пары аргументов: -DEFAULT => 'начальное значение' И -MAXLENGTH => п, где n — максимальное число символов, которое может принять данное поле.

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

print h2("Prior Messages");

foreach $entry (Sentries) {

printf("%s [%s]: %s",

$entry->param("date"),

$entry->param("name"),

$entry->param("message"));

print br ();

} print end_html;

Как вы, без сомнения, догадываетесь, функция h2 задает HTML-заголовок второго уровня. В остальной части кода мы просто последовательно формируем текущий список сохраняемых записей (это тот же список, который мы ранее записали в сохраняемый файл), выводя из каждой дату, имя и сообщение.

Пользователи могут работать с этой формой гостевой книги, непрерывно набирая сообщения и нажимая кнопку передачи. Это имитирует электронную доску объявлений, позволяя пользователям видеть новые сообщения друг друга сразу же после их передачи. Общаясь друг с другом подобным образом, пользователи многократно вызывают одну и ту же CGI-программу;

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



Создание для файла альтернативных имен связывание ссылками



Создание для файла альтернативных имен: связывание ссылками

Иногда пользователю нужно, чтобы у файла было два, три, а то и дюжина имен (как будто одного имени файлу не хватает!). Операция присвоєння файлу альтернативних имен называется создание ссылок. Две основних формы создания ссылок — ато создание жестких ссылок и создание симво-лических (или мягких) ссылок. Не все виды файлових систем подцерживают оба типа сеилок или хотя би один из них. В зтом разделе описани файловие системи, соответствующие стандарту POSIX.

Жесткие и символические ссылки

Жесткая ссылка на файл неотличима от его исходного имени; ни одна из жестких ссилок не является более "реальним именем" для файла, чем любая другая.

Операционная система следит за тем, сколько жестких ссылок обознача-ют файл в кажднй данний момент времени. Когда файл впервые создается, у него имеется одна ссылка. Каждая новая жесткая ссылка увеличивает зто число, а каждая удаленная — уменьшает. Когда исчезает последняя ссылка на файл и файл закрнвается, он прекращает своє существование.

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

В большинстве систем применение жестких ссилок для каталогов огра-ничено. Чтобы структура каталогов имела вид дерева, а не произвольную форму, для каталога допускается наличие только одного имени от корня, ссылки из "точечного" файла на данный каталог, и семейства жестких ссылок "точка-точка" из каждого из его подкаталогов. Если ви попитаєтесь создать еще одну жесткую ссылку на каталог, то получите сообщение об ошибке (если только вы не привилегированный пользователь — тогда вам придется провести всю ночь за восстановлением своей поврежденной фай-ловой системи).

Символическая ссылка — зто файл особого вида, который содержит в качестве данных путевое имя. Когда зтот файл открывается, операционная система рассматривает его содержимое как заменяющие символы для дан-ного путевого имени и заставляет ядро еще немного порыскать по дереву каталогов, используя новое имя.

Например, если символическая ссылка fred содержит имя barney, то указание открыть файл fred — зто, на самом деле, указание открыть файл barney. Если barney — каталог, то fred/wilma обозначает Ьатеу/wilma.

Содержимое символической ссылки (i.e. имя, на которое указывает символическая ссылка) не обязательно должно обозначать существующий файл или каталог. В момент, когда создаетсяу/'erf, существование barney вовсе не обязательно. Более того, Ьатеу может никогда и не появиться! Содержимое символической ссылки может указывать на путь, который ведет за пределы текущей файловой системи, позтому можно создавать символиче-скую ссылку на файл, находящийся в другой смонтированной файловой системо.

Отслеживая новое имя, ядро может натолкнуться на другую символиче-скую ссылку. Зта новая ссылка содержит новые злементы отслеживаемого пуги. Одни символические ссылки могут указывать на другие символические ссылки. Как правило, допускается до восьми уровней символических ссылок, но на практико такое встречается редко.

Жесткая ссылка защищает содержимое файла от уничтожения (потому что она считается одним из имен файла). Символическая же ссылка не может уберечь содержимое файла от исчезновения. Символическая ссылка может указывать на другие смонтированные файловые системи, а жесткая — не может. Для каталога может бить создана только символическая ссылка.

Создание жесткиж и символических ссылок в Perl

В ОС UNIX жесткие ссылки создают с помощью команди In. Например, команда

In fred bigdumbguy

позволяет создать жесткую ссилку из файла fred (которнй должен существо-вать) на bigdumbguy. В Per! зто виражается так:

link("fred","bigdumbguy") || die "cannot link fred to bigdumbguy";

Функция link принимает два параметра — старое имя файла и новий псевдоним для зтого файла. Если ссылка создана успешно, link возвращает значение "истина". Как и команда mv, UNIX-команда In позволяет указывать в качестве псевдонима только каталог (без имени файла). Функция link (как и функция rename) не настолько умна, позтому вы должны указывать полное имя файла явно.

При создании жесткой ссылки старое имя не может быть именем каталога*, а новый псевдоним должен указывать на ту же файловую систему. (Зти ограничения частично обусловили необходимость создания символи-ческих ссылок.)

В системах, которые поддерживают символические ссылки, в команде In может использоваться опция -s, которая создает символическую ссылку. Например, если необходимо создать символическую ссылку из barney на neighbor (чтобы обращение к neighbor фактичесїзі было обращением к barney), следует использовать команду

In -s barney neighbor

В Perl для зтого применяется функция symlink:

symlinkf"barney","neighbor") || die "cannot symlink to neighbor";

Отметим, что barney не обязательно должен существовать — ни сейчас, ни в будущем. В этом случае обращение к neighbor возвратит нечто туманное вроде No such file or directory.

Когда вы вызываете Is -1 для каталога, содержащего символическую ссылку, вы получаете как имя зтой ссылки, так и информацию о том, куда она указывает. В Perl зту же информацию можно получить с помощью функции readlink, которая по принципу работы удивительно похожа на системний вызов с тем же именем: она возвращает имя, на которое указывает заданная символическая ссылка. Так, в результате выполнения операции

if (defined($х = readlink("neighbor"))) ( print "neighbor points at '$x'\n";

вы получите сведения о barney, если все нормально. Если выбранная символическая ссылка не существует, не может быть прочитана или вообще не является таковой, readlink возвращает undef (т.е. вданном случае "ложь") — именно позтому мы ее здесь и проверяем.

В системах, не поддерживающих символические ссылки, обе функции — и symlink, и readlink — потерпят неудачу и выдадут сообщения об ошибке. Perl может "спрятать" от вас некоторые зависящие от конкретной системи особенности, но некоторые все равно проявляются. Зто как раз одна из них.

* Если только вы не привелигированный пользователь и не любите забавляться с командой fsck, восстановливая поврежденную файловую систему.



Создание и удаление каталогов


mkdir ("gravelpit",0777) || die "cannot mkdir gravelpit: $!";

UNIX-команда rmdir(l) удаляет пустые каталоги. В Perl єсть ее зквивалент с тем же именем. Вот как можно сделать Фреда безработным:

rmdir ("gravelpit") I| die "cannot rmdir gravelpit: $!";

Хотя зти Perl-операции используют преимущества системных вызовов с такими же именами, они будут вьшолняться (хотя и чуточку медленнее) даже в системах, не поддерживающих такие вызовы. Perl вызывает утилиты mkdir и rmdir (или как там они называются у вас в системо) автоматически.



Создаваемые операцией my() переменные файлового уровня



Создаваемые операцией my() переменные файлового уровня

Операцию my () можно также использовать на внешнем уровне программы, вне всех подпрограмм и блоков. Хотя в результате не будет получена "локальная" переменная в описанном выше смысле, это может оказаться достаточно полезным, особенно при использовании в сочетании с Рет\-прагмой*

use strict;

* Прагма — это директива компилятора. Среди этих директив — директивы задания целочисленных арифметических операций, перегрузки числовых операций, запрашивания дополнительных текстовых предупреждений и сообщений об ошибках. Эти директивы описаны в главе 7 книги Programming Perl и на man-странице perlmodlib(l).

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

use strict;

my $а; # сначала значение undef my @b = qw(fred barney betty); # присвоить начальное значение

push @b, qw(wilma); # разве можно забыть про Вильму? @с = sort @b; # HE КОМПИЛИРУЕТСЯ

Во время компиляции последний оператор будет помечен флагом ошибки, потому что он обратился к переменной, не объявленной ранее с помощью операции my (т.е. @с). Другими словами, ваша программа даже не начнет работать до тех пор, пока не будут объявлены все используемые переменные.

Преимущества принудительного объявления переменных таковы:

1. Ваши программы будут работать несколько быстрее (обращение к переменным, созданным с помощью my, производится несколько быстрее, чем к обычным переменным).*

2. Гораздо быстрее будут выявляться ошибки набора, потому что вы больше

не сможете случайно обратиться к несуществующей переменной $ freed,

когда вам будет нужна переменная $fred.

По этой причине многие программисты, пишущие на Perl, автоматически начинают каждую новую программу прагмой use strict.



Список и массив Список — это упорядоченные


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









Срезы хешей



Срезы хешей

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

$score("fred"} - 205;

$score("barney"} = 195;

$scoref"dino"} = 30;

Все это можно записать одной строкой:

($score("fred"},$score("barney"),$score("dino")) ° (205,195,30);

Но даже она слишком длинна, поэтому давайте используем срез хеша:

$score{"fred","barney","dino"} = (205,195,30);

Вот так гораздо короче. Можно сочетать использование среза хеша и интерполяции переменных:

@players = qwffred barney dino);

print "scores are: @score(@players}\n";

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

@league{keys %score( = values %score;

Здесь значения хеша %score сливаются с хешем %league. Это эквивалентно выполнению гораздо более медленной операции:

%league = (%league, %score); = # слить %score с %league



Ссылки



Ссылки









Стандартные модули



Стандартные модули

Ниже приведен перечень всех Perl-прагм и модулей, входящих в текущий дистрибутив языка (версия 5.004). Классификация модулей произвольная.

Таблица Б. 1. Общее программирование: разное

Модуль Функция
autouse Задерживает загрузку модуля до его использования
constant Создает константы периода компиляции
Benchmark Проверяет и сравнивает временные параметры выполне
ния кода
Config Позволяет получить информацию о конфигурации Perl
Env Импортирует переменные среды
English Для пунктуационных переменных использует английские имена или имена на языке awk
FindBin Находит путь к выполняемой в данный момент программе
Getopt::Long Осуществляет расширенную обработку опций командной
строки
Getopt::Std Обрабатывает односимвольные ключи и осуществляет их
кластеризацию
lib Манипулирует массивом @INC во время компиляции
Shell Запускает команды shell прозрачно для Perl
strict Ограничивает использование небезопасных конструкций
Symbol Генерирует анонимное развертывание (glob); уточняет
имена переменных
subs Предопределяет имена подпрограмм
vars Предопределяет имена глобальных переменных

Таблица Б. 2. Общее программирование: обработка ошибок и регистрация

Модуль Функция
Сагр diagnostics sigtrap

Sys::Syslog

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

Разрешает обратное прослеживание стека для неожиданных сигналов

Perl-интерфейс к UNIX-вызовам syslog(3)

Таблица Б.З. Общее программирование: доступ к файлам и их обработка
Модуль Функция
Cwd DirHandle

Fcnti File::Basename File::CheckTree File:: Copy File::Find File::Path FileCache

FileHandle SelectSaver

Получает путевое имя текущего рабочего каталога

Выдает методы объектов для работы с дескрипторами каталогов

Загружает С-определения Fcntl.h Разбирает спецификации файлов Выполняет всевозможные проверки для набора файлов Копирует файлы или дескрипторы файлов Обеспечивает просмотр дерева файлов Создает и удаляет ряд каталогов

Позволяет одновременно открывать больше файлов, чем разрешает система

Выдает методы объектов для работы с дескрипторами файлов

Сохраняет и восстанавливает выбранный дескриптор файла

Таблица Б. 4. Общее программирование: классы для операций ввода-вывода
Модуль Функция
10 IO::File IO::Handle IO::Pipe IO::Seekable IO::Select IO::Socket Интерфейс верхнего уровня к классам 10::* Методы объектов для работы с дескрипторами файлов Методы объектов для дескрипторов ввода-вывода Методы объектов для каналов Методы для объектов ввода-вывода на базе поиска Объектный интерфейс для выбора Объектный интерфейс для портов
Таблица Б. 5. Общее программирование: обработка текста и экранные интерфейсы
Модуль Функция
locale

Pod::HTML Pod::Text

Search::Dict Term::Cap

Использует локализацию POSIX для встроенных операций

Конвертирует pod-данные в HTML

Конвертирует pod-данные в форматированный ASCII-текст

Ищет ключ в файле словаря Интерфейс termcap

Модуль Функция
Term::Complete Text::Abbrev Text::ParseWords Text::Soundex Text::Tabs Text::Wrap Модуль завершения слов Создает из списка таблицу сокращений Разбирает текст на лексемы и создает из них массив Реализует алгоритм Soundex, разработанный Кнутом Раскрывает и сворачивает знаки табуляции Выделяет текст в абзац
Таблица Б. 6. Интерфейсы к базам данных
Модуль Функция
AnyDBMFile DBFile GDBMFile NDBMFile ODBMFile SDBM File Создает основу для множества DBM Доступ к Berkeley DB Связанный доступ к библиотеке GDBM Связанный доступ к файлам NDBM Связанный доступ к файлам ODBM Связанный доступ к файлам SDBM
Таблица Б. 7. Математика
Модуль Функция
Integer Math::BigFloat Math::BigInt Math::Complex Выполняет арифметические операции в целочисленном формате, а не в формате с двойной точностью

Пакет математических операций для чисел с плавающей запятой произвольной длины

Пакет математических операций для целых чисел произвольной длины

Пакет для комплексных чисел

Таблица Б. 8. World Wide Web
Модуль Функция
CGI

CGI::Apache CGI::Carp CGI::Fast CGI::Push CGI:: Switch

Интерфейс Web-сервера (Common Gateway Interface) Поддержка Perl-модуля сервера Apache Ошибки сервера регистрации с полезной информацией Поддержка FastCGI (устойчивый серверный процесс) Поддержка "выталкивания" со стороны сервера Простой интерфейс для многих типов серверов
Таблица Б. 9. Сети и межпроцессное взаимодействие
Модуль Функция
1РС::Ореп2 Открывает процесс для чтения и записи
IPC::Open3 Открывает процесс для чтения, записи и обработки ошибок
Net::Ping Проверяет, есть ли данный хост в сети
Socket Загружает С-определения socket.h и манипуляторы структур
Sys::Hostname Пытается получить хост-имя всеми возможными способами
Таблица Б. 10. Автоматизированный доступ к Comprehensive Perl Archive Network
Модуль Функция
CPAN Простой интерфейс к CPAN
CPAN::FirstTime Утилита для создания файла конфигурации CPAN
CPAN::Nox Запускает CPAN, избегая компилированных расширений
Таблица Б. 11. Время и локализация
Модуль Функция
Time:: Local Эффективно определяет местное и среднее гринвичское
время
I18N::Collate Сравнивает восьмибитовые скалярные данные
Таблица Б. 12. Объектные интерфейсы к встроенным функциям
Модуль Функция
Class::Struct Объявляет struct-подобные типы данных как Perl-классы
File::stat Объектный интерфейс к функции stat
Net::hostent Объектный интерфейс к функциям gethost*
Net::netent Объектный интерфейс к функциям getnet*
Net::protoent Объектный интерфейс к функциям getproto*
Net::servent Объектный интерфейс к функциям getserv*
Time::gmtime Объектный интерфейс к функции gmtime
Time::localtime Объектный интерфейс к функции localtime
Time::tm Внутренний объект для Time::{gm,local}time
User::grent Объектный интерфейс к функциям getgr*
User::pwent Объектный интерфейс к функциям getpw*
Таблица Б. 13. Для разработчиков: автозагрузка и динамическая загрузка
Модуль Функция
Autoloader Загружает функции только по требованию
AutoSplit Разбивает пакет для автозагрузки
Devel::SelfStubber Генерирует заглушки для модуля SelfLoading
DynaLoader Автоматическая динамическая загрузка Perl-модулей
SelfLoader Загружает функции только по требованию
Таблица Б. 14. Для разработчиков: расширения языка и поддержка разработки платформ
Модуль Функция
blib Определяет структуру каталогов blib во время построения модулей
ExtUtils::Embed Утилиты для встраивания Perl в С-программы
ExtUtils::Install Инсталлирует файлы
ExtUtils::Liblist Определяет библиотеки для использования и по
рядок их использования
ExtUtils:: MakeMaker Создает Makefile для расширения Perl
ExtUtils: '.Manifest Утилиты для написания и проверки файла MANIFEST
ExtUtils::Miniperl Создает С-код для perlmain.c
ExtUtils: :Mkbootstrap Создает файл самозагрузки для использования модулем DynaLoader
ExtUtils: :Mksymlists Пишет файлы опций компоновщика для динами
ческого расширения
ExtUtils: :MMOS2 Методы для отмены UNIX-режима в ExtUtils::MakeMaker
ExtUtils: :MMUnix Методы, используемые модулем ExtUtils::MakeMaker
ExtUtils::MMVMS Методы для отмены UNIX-режима в ExtUtils::MakeMaker
ExtUtils: :testlib Исправляет @INC для использования только что
созданного расширения
Opcode Блокирует коды операций при компиляции Perl-
кода
ops Прагма для использования с модулем Opcode
POSIX Интерфейс к стандарту IEEE 1003.1
Safe Создает защищенные пространства имен для оценки Perl-кода
Test:: Harness Выполняет стандартные тестовые Perl-сценарии со сбором статистических данных
vmsish Обеспечивает возможности, характерные для VMS
Таблица Б. 15. Для разработчиков: поддержка объектно-ориентированного программирования
Модуль Функция
Exporter Стандартный метод импорта для модулей
overload Перегружает математические операции Perl
Tie::RefHash Базовый класс для связанных хешей со ссылками в
качестве ключей
Tie:: Hash Содержит определения базового класса для связанных хешей
Tie:: Scalar Содержит определения базового класса для связанных
скаляров
Tie::StdHash Содержит определения базового класса для связанных хешей
Tie::StdScalar Содержит определения базового класса для связанных
скаляров
Tie::SubstrHash Обеспечивает хеширование с фиксированным размером таблицы и фиксированной длиной ключей
UNIVERSAL Базовый класс для всех классов


<STDIN> как массив Одна


$а = <STDIN>; # читать стандартный ввод в списочном контексте

Если пользователь, выполняющий программу, введет три строки и нажмет [Ctrl+D]* (чтобы обозначить конец файла), массив будет состоять из трех элементов. Каждый из них является строкой, заканчивающейся символом новой строки, и соответствует введенной пользователем строке.

* В некоторых системах конец файла обозначается нажатием клавиш [Ctrl+Z], а в других эта комбинация служит для приостановки выполняемого процесса.



<STDIN> как скалярное значение



<STDIN> как скалярное значение

Если вы — типичный хакер, то, вероятно, давно уже задаетесь вопросом:

а как же ввести в Perl-программу какое-либо значение? Вот самый простой способ. Каждый раз, когда там, где требуется скалярное значение, вы используете дескриптор <STDIN>, Perl читает следующую полную строку текста со стандартного ввода (до первого символа новой строки) и использует ее в качестве значения этого дескриптора. Термином "стандартный ввод" может обозначаться многое, но если вы не делаете в своей программе ничего необычного, это означает терминал пользователя, вызвавшего вашу программу (вероятнее всего, ваш собственный терминал). Если строки, которую можно было бы прочитать, еще нет (типичный случай, если только вы заранее не набрали полную строку), Perl-программа останавливается и ждет, пока вы не введете какие-нибудь символы и вслед за ними символ перехода на новую строку.

В конце строкового значения дескриптора <STDIN> обычно стоит символ новой строки. Чаще всего от этого символа нужно избавляться сразу же (ведь между hello Hhello\n — большая разница). Здесь и приходит на помощь знакомая нам функция chomp. Типичная последовательность ввода выглядит примерно так:

$а = <STDIN>; # читаем текст chomp($a); # избавляемся от надоедливого символа новой строки

* Вы, возможно, придете к выводу, что легче воспользоваться функциями

uc, ucfirst, lc И Icfirst.

Общепринятое сокращение этих двух строк выглядит так:

chomp($a = <STDIN>) ;

Присваивание внутри круглых скобок продолжает относиться к $а даже после того, как этой переменной уже присвоено значение. Таким образом, функция chomp оперирует с переменной $а. (Это вообще справедливо для операции присваивания; присваиваемое выражение можно использовать везде, где необходима переменная, а действия относятся к переменной, стоящей слева от знака равенства.)

Вывод с помощью функции print

Итак, мы вводим значения с помощью дескриптора <stdin>. А как их вывести из программы? С помощью функции print. Эта функция принимает значения, стоящие в скобках, и выдает их без всяких украшений на стандартный вывод. Стандартный вывод — это опять-таки ваш терминал (если вы не делаете в программе ничего необычного). Например:

print("hello world\n"); # выводится hello world с символом новой строки print "hello world\n"; # то же самое

Обратите внимание на второй пример, где показана форма функции print без круглых скобок. Использовать скобки или нет — это, главным образом, вопрос стиля и быстроты набора, но есть несколько случаев, где скобки обязательны во избежание двусмысленности.

Мы увидим, что для функции print можно задавать список значений, но поскольку про списки мы еще не говорили, отложим этот вопрос до главы 6.

Значение undef

Что будет, если использовать скалярную переменную до того, как ей присвоено значение? Ничего серьезного, в общем-то, не произойдет. До присваивания значения переменные имеют значение undef. При использовании в качестве числа это значение выглядит как нуль, а при использовании в качестве строки —- как пустая строка нулевой длины. Впрочем, при работе с ключом -w вы получите предупреждение — это хороший способ вылавливания ошибок программирования.

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

Одна из уже знакомых нам операций, которая в определенных обстоятельствах возвращает undef — это операция <stdin>. Обычно она возвращает следующую из прочитанных строк; если же строк для чтения больше нет (например, когда вы нажали на терминале клавиши [Ctri+D] или когда в файле больше нет данных), то <stdin> возвращает значение undef. В главе 6 мы посмотрим, как осуществить проверку в данной ситуации и выполнить специальные действия, если данных для чтения больше нет.



Сводка операций проводимых над процессами



Сводка операций, проводимых над процессами

Операции, служащие для запуска процессов, перечислены в таблице 14.1. Таблица 14.1. Операции запуска процессов

Операция Стандартний ввод Стандартний

вывод

Стандартний вывод ошибок Нужно ли ожидать завершення процесса
System() Наследуется Наследуется Наследуется Да
от программы от программы от программы
Строка в обратных Наследуется от программы Принимается как строковое Наследуется от программы Да
кавычках значение
Запуск Соединен с Наследуется Наследуется Только во
процесса как деск дескриптором файла от программы от программы время вы-полнения
риптора файла для close ()
вывода при
помощи
команди
open()
Запуск Наследуется Соединен с Наследуется Только во
процесса как деск от программы дескриптором файла от программы время вы-полнения
риптора файла для close ()
ввода при
помощи
команди
open()
fork, Выбирается Выбирается Выбирается Выбирается
ехес, пользователем пользователем пользователем пользователем
wait,
waitpid

Самый простой способ создать процесе — использовать для зтого функ-цию system. На стандартный ввод, вывод и вывод ошибок зто не влияет (они наследуются от Perl-процесса). Строка в обратных кавычках создает процесе и передает данные со стандартного вывода зтого процесса как строковое значение для Perl-программы. Стандартный ввод и стандартный вывод ошибок не изменяются. Оба зти метода требуют завершення процесса до выполнения другого кода.

Простой способ получить асинхронний процесе (процесе, который по-зволяет продолжать выполнение Perl-программы до своего завершення) — открыть команду как дескриптор файла с созданием канала для стандартного ввода или стандартного вывода зтой команди. Команда, открытая как дескриптор файла для чтения, наследует стандартный ввод и стандартный вывод ошибок от Perl-программы; команда, открытая как дескриптор файла для записи, наследует от Perl-программы стандартный вывод и стандартный вывод ошибок.

Самый гибкий способ запустить процесе — заставить программу вызвать функции fork, ехес и wait или waitpid, которые полностью соответст-вуют своим UNIX-тезкам. С помощью зтих функции вы можете запустить какой-либо процесе синхронно или асинхронне, а также конфигурировать по своєму усмотрению стандартный ввод, стандартный вывод и стандартный вывод ошибок*.



Телеконференции Usenet


    Телеконференции по Perl — неиссякаемый источник информации (прав-да, иногда беспорядочной) о языке. Телеконференция comp.lang.perl.an-nounce — с низким трафиком, используется для публикации объявлений, связанных с Perl. Эти объявления часто касаются выпуска новых версий, исправления ошибок, новых расширений и модулей, часто задаваемых вопросов (FAQ).
    В телеконференции сотр.lang.perl.misc, уровень трафика в которой очень высок, обсуждается практически все — от технических вопросов и филосо-фии Perl до Perl-игр и Perl-поэзии. Как и сам Perl, эта телеконференция слывет полезной, и ни один вопрос не считается слишком глупым, чтобы его нельзя было задавать*.
    В телеконференции comp.lang.perl.tk обсуждаются вопросы использования популярного набора инструментальных средств Tk, входящего в состав Perl. Телеконференция comp.lang.perl.modules посвящена разработке и использова-нию Perl-модулей — самого лучшего средства получения многократно ис-пользуемого кода. Когда вы будете читать эти строки, возможно, уже появятся и другие телеконференции.
    Есть еще одна телеконференция, к материалам которой вы, может быть, захотите обратиться (по крайней мере если занимаетесь CGI-программиро-ванием в Web) — сотр.mfosystems.www.authonng.cgi. Хотя, строго говоря, эта конференция и не посвящена Perl как таковому, большинство обсуждаемых там программ написаны на этом языке. Именно к ее материалам следует обращаться по вопросам, связанным с применением Perl в World Wide Web.

* Разумеется, некоторые вопросы слишком просты, чтобы на них отвечать, особенно те, на которые уже даны ответы в FAQ.







Темы которых мы не коснулись



Темы, которых мы не коснулись

Как ни странно, даже при таком объеме книги некоторые вопросы все равно остались незатронутыми. Данное приложение содержит полезную дополнительную информацию.

Назначение этого раздела — не обучить вас тем вещам, которые перечислены здесь, а просто дать их перечень. За дальнейшей информацией обращайтесь к книге Programming Perl, на man-страницы perl(l) и perlfaq(l), к HTML-документам, имеющимся в каталоге doc архива CPAN, и к материалам телеконференций Usenet.









Терминология



Терминология

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

Пакет

Пакет — это простое устройство управления пространством имен, позволяющее в каждой из двух разных частей Perl-программы иметь свою переменную с именем $fred. Этими пространствами имен управляет объявление package, описанное в главе 5 книги Programming Perl.

Библиотека

Библиотека — это набор подпрограмм определенного назначения. Часто библиотека объявляет себя отдельным пакетом; это позволяет держать в одном месте соответствующие переменные и подпрограммы, чтобы они не мешали другим переменным в вашей программе. Как правило, библиотека старого стиля размещалась в отдельном файле, часто под именем с расширением р1. Библиотечные программы включались в основную программу посредством функции require. He так давно этот подход был заменен использованием модулей (см. следующий абзац), и термин библиотека теперь часто обозначает всю систему модулей, которые поставляются с Perl.

Модуль

Модуль — это библиотека, соответствующая конкретным соглашениям, которая позволяет включать библиотечные подпрограммы в основную программу во время компиляции с помощью директивы use. Имена файлов модулей имеют расширение рт, потому что это необходимо для корректного использования директивы use. Подробно Perl-модули описаны в главе 5 книги Programming Perl.

Прагма

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



Транслитерация Если вам необходимо


Такое преобразование данных очень просто выполняется в shell с помощью стандартной команды tr(l):

tr ab ba <indata >outdata

(Если вы ничего не знаєте о командо tr, загляните на man-страницу tr(l);

зто полезный инструмент.) В Perl тоже применяется операция tr, которая работает в основном так же:

tr/ab/ba;

Операция tr принимает два аргумента: старая_строка и новая_строка. Они используются так же, как аргументы команды s///; другими словами, имеется некий разделитель, который стоит сразу же за ключевым словом tr и разделяет и завершает аргументы (в данном случае зто косая черта, но в зтой роли могут виступать почти все символы).

Аргументи операции tr похожи на аргументи команды tr(l). Операция tr изменяет содержимое переменной $_ (совсем как si/I}, отыскивая в ней символы старой строки и заменяя найденные символы соответствующи-ми символами новой строки. Вот несколько примеров:

$_ = "fred and barney";

tr/fb/bf; # $ теперь содержит "bred and farney" tr/abcde/ABCDE/; t $_ теперь содержит "BrED AnD fArnEy" tr/a-z/A-Z/; # $_ теперь содержит "BRED AND FARNEY"

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

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

$_ = "fred and barney";

tr/a-z/x/; # $_ теперь содержит "хххх xxx xxxxxx"

Чтобы такое не происходило, поставьте в конце операции tr/// букву d, которая означает delete ("удалить"). В данном случае последний символ не повторяется. Все символы старой строки, для которых нет соответствую-щих символов в новой строке, просто удаляются:

$ = "fred and barney";

tr/a-z/ABCDE/d; # $_ теперь содержит "ED AD BAE"

Обратите внимание на то, что все буквы, стоящие после буквы е, исчезают, потому что в новом списке соответствующей буквы нет, и на то, что на пробелы зто не влияет, потому что их нет в старом списке. По принципу работы зто зквивалентно команде tr c опцией -d.

Если новый список пуст и опция d не используется, то новый список будет совпадать со старым. Зто может показаться глупым — зачем заменять І на І и 2 на 2? — но на самом деле в зтом єсть довольно глубокий смысл. Операция tr/// возвращает количество символов, совпавших со старой строкой, и путем замены символов на самих себя вы можете получить число таких символов, содержащихся в новой строке*. Например:

* Зто справедливо только для одиночних символов. Для подсчета строк в операции сопоставления с образцом используйте флаг /д:

while (/образец/д) { $count++;

>

$_ = "fred and barney";

$count = tr/a-z//; # $_ не изменилась, но $count = 13 $count2 = tr/a-z/A-Z/; # $_ переведена в верхний регистр, и $count2 = 13

Если в конце операции добавить букву с (как мы добавляли букву d), символы старой строки будут рассматриваться как исключение из набора всех 256 символов. Каждый символ, указаними в старой строке, удаляется из совокупности всех возможных символов; оставшиеся символы, взятые по порядку от младшего к старшему, образуют новую строку-результат. Напри-мер, подсчитать или изменить в нашей строке все символы, не являющиеся буквами, можно так:

$_ = "fred and barney";

$count == tr/a-z//c; # $_ не изменилась, но $count = 2

tr/a-z/_/c; # $_ теперь содержит "fred_and_barney" (символы-небуквы => _)

tr/a-z//cd; t $_ теперь содержит "fredandbarney" (символы-небуквы

удалены)

Отметим, что зти опции можно обьединять, как показано в последнем примере, где мы сначала меняем совокупность символов на "дополняющую" (список букв становится списком всех символов-небукв), а затем с помощью опции d удаляем все символы зтой совокупности.

Последняя опция операции tr/// — s, которая заменяет множество зкземпляров одной преобразованной буквы одним. Например:

$_ = "aaabbbcccdefghi";

tr/defghi/abcddd/s; # $_ теперь содержит "aaabbbcccabcd"

Обратите внимание: буквы def заменены на abc, a ghi (которые без опции s превратились би в ddd) стали одной буквой d. Отметим также, что стоящие друг за другом буквы в первой части строки "не сжимаются", потому что для них не задано преобразование. Вот еще несколько примеров:

$_ = "fred and barney, wilma and betty";

tr/a-z/X/s; ” $_ теперь содержит "X X X, X X X" $_ = "fred and barney, wilma and betty";

tr/a-z/_/cs; # $_ теперь содержит "fred_and_barney_wilma and betty"

В первом из зтих примеров каждое слово (стоящие друг за другом буквы) было заменено одной буквой х. Во втором примере все группы стоящихдруг за другом символов-небукв стали одиночними знаками подчеркивания.

Как и команда s///, операция tr может бьггь проведена над другой строкой, а не только над строкой, хранящейся в переменной $_. Зто достигается с помощью операции =~:

$names = "fred and barney";

$names =~ tr/aeiou/X/; # $names теперь содержит "frXd Xnd bXrnXy"



Удаление файла Вы уже научились


Perl-функция unlink (названная по имени системного вызова POSIX) удаляет одно из имен файла (которнй может иметь и другие имена). Когда удаляется последнее имя файла и ни в одном процессе он не открыт, удаляется и сам файл. Зто в точности соответствует тому, что делает UNIX-команда гт. Поскольку у файла часто бывает только одно имя (если ви не создавали жесткие сснлки), то в большинстве случаев удаление имени можно считать удалением файла. Приняв зто допущенне, покажем, как удалить файл fred, а затем удалить файл, имя которого вводится во время выполнения программы:

unlink ("fred"); # распрощаемся с файлом fred print "what file do you want to delete? ";

chomp($name = <STDIN>) ;

unlink ($name) ;

Функция unlink может принимать также список имен, подлежащих удалению:

unlink ("cowbird","starling"); # убьем двух зайцев unlink <*.о>; # как "rm *.o" в shell

Операция <*. o> выполняется в списочном контексте и создает список имен файлов, которые совпадают с образцом. Зто именно то, что нужно передать в unlink.

Функция unlink возвращает количество успешно удаленных файлов. Если указан только один аргумент и соответствующий ему файл удаляется, то возвращается единица; в противном случае возвращается нуль. Если заданы имена трех файлов, но удалить можно только два, то возвращается два. Установить, какие именно файлы были удалены, на оснований возвра-щенного значення невозможно, позтому если вам нужно определить, какой файл не удален, удаляйте их по одному. Вот как производится удаление всех обьектных файлов (имена которых заканчиваются на . о) с выдачей сообще-ния об ошибке для каждого файла, который удалить нельзя:

foreach $file (<*.o>) ( # пройти по списку .о-файлов

unlink($file) || warn "having trouble deleting $file: $!";

1

Если unlink возвращает 1 (зто означает, что единственный указанный файл был удален), то функция warn пропускается. Если имя файла не может быть удалено, результат "О" означает "ложь", позтому warn выполняется. Абстрактно зто можно прочитать так: "удали зтот файл или сообщи мне о нем".

Если функция unlink дается без аргументов, то по умолчанию вновь используетея переменная $_. Так, приведеними выше цикл можно записать следующим образом:

foreach (<*.о>) ( # пройти по списку .о-файлов

unlink || warn "having trouble deleting $_: $!";



Упаковка и распаковка двоичных


По зтой причино написанные на Perl сетевые программы, ожидающие или возвращающие IP-адрес, используют четырехбайтовую строку, одному символу которой соответстввует один байт в памяти. Хотя конструирование и интерпретация такой байтовой строки — довольно простая задача, решае-мая с помощью функций chr и ord (здесь не представленных), в Perl используется более зффективное решение, которое в равной степени при-менимо и к более сложньш структурам.

Функция pack по принципу работы немного похожа на функцию sprintf. Она получает строку, задающую формат, и список значений и упаковывает значення в одну строку. Однако в pack строка, задающая формат, предназначена для создания двоичной структури данных. Напри-мер, вот как можно взять четыре небольших целых числа и упаковать их в виде последовательности байтов без знака в строке:

5buf = packf'CCCC", 140, 186, 65, 25);

Здесь строка формата pack — четыре буквы С. Каждая С соответствует отдельному значенню, взятому из приведенного следом списка (подобно тому, что делает спецификация % в функций sprintf). Формат С (согласно man-страницам Perl, краткому справочнику, книге Programming Perl, HTML-файлам и даже видеоролику Perl: The Motion Picture) обозначает один байт, вычисляемый из символьного значення без знака (короткого целого). Отрока-результат в переменной $buf представляет собой четырехсимвольную строку, в которой каждый символ задан одним байтом. Зти байты имеют значення 140, 186, 65 и 25 соответственно.

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

$buf = packC'l",0х41424344) ;

генерирует четырехсимвольную строку, состоящую из символов abcd или dcba -- в зависимости от того, какой порядок хранения байтов используется на данной машине: "младший в младшем" или "старший в младшем" (либо что-то совершенно иное, если зта машина "не говорит" на ASCII). Зто обіясняется тем, что мы упаковываем одно значение в четыре символа (для представлення длинного целого отводится четыре байта), а зто одно значение как раз состоит из байтов, представляющих коды ASCII первых четырех букв алфавита. Аналогичньш образом,

$buf = pack("ll", 0х41424344, 0х45464748);

создает восьмибайтовую строку, состоящую из букв abcdefgh или dcbahgfe, опять-таки в зависимости от того, какой порядок хранения байтов используется в данной машине — "младший в младшем" или "старший в младшем".

Полный перечень различных форматов, используемых для упаковки, приведен в справочной документации (perlfunc(l) или Programming Perl). Мы приведем некоторые из них как примеры, но все, конечно, давать не будем.

Допустим, вам дали восьмибайтовую строку abcdefgh и сказали, что она является представлением хранящихся в памяти (один символ — один байт) двух длинных (четырехбайтовых) значений со знаком. Как ее интерпретиро-вать? Нужно воспользоваться функцией, обратной функции pack,— функ-цией unpack. Она берет строку управления форматом (как правило, иден-тичную той, которую вы указывали в функции pack) и строку данных и возвращает список значений, которые хранятся в соответствующих ячейках памяти. Давайте, например, распакуем такую строку:

($vall,$val2) = unpack("ll","ABCDEFGH");

Зто даст нам в переменной $vall нечто вроде 0х41424344, а может быть, и 0х44434241 (в зависимости от порядка хранения байтов). По сути дела, по возвращаемым значенням мы можем определить, на какой машине работаем — с порядком "младший в младшем" или "старший в младшем".

Пробельные символы в строке, задающей формат, игнорируются и ис-пользуются лишь для удобочитаемости. Число в зтой строке, как правило, задает повторение предндущей спецификации соответствующее количество раз. Например, сссс можно записать как С4 или С2С2, смысл от зтого не изменится. (Однако в некоторых спецификациях число, указанное после символа, задающего формат, является частью спецификации, позтому их подобным образом записывать нельзя.)

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

$bu? ” pack("C*", 140, 186, 65, 25);

Здесь указанные четыре значення полностью обрабатываются одной спецификацией формата. Если бы вам требовались два коротких целых и "максимально возможное количество символов без знака", то можно было бы написать примерно так:

$buf = pack("s2 C*", 3141, 5926, 5, 3, 5, 8, 9, 7, 9, 3, 2);

Здесь мы получаем первые два значення как короткие (й генерируем, вероятно, четыре или восемь символов), а остальные девять — как символы без знака (й генерируем, почти наверняка, девять символов).

Функция unpack co звездочкой в качестве спецификации может форми-ровать список злементов, длина которых заранее не определена. Например, при распаковке с использованием формата с* создается один злемент списка (число) для каждого символа строки. Так, оператор

@values = unpack("С*", "hello, world!\n");

позволяет сформировать список из 14 злементов, по одному для каждого символа строки.



Управление процессами



Управление процессами









Управляющие структуры



Управляющие структуры









Большинство глав завершаются упражнениями, ответы



Упражнение

Большинство глав завершаются упражнениями, ответы к которым даются в приложении А. Для этой главы ответы уже были даны выше.

1. Наберите программы-примеры и заставьте их работать. (Вам понадобится создать списки секретных слов.) Если потребуется помощь — обратитесь к местному Perl-гуру.

Упражнение Ответ см в приложении


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










Напишите программу, которая создает таблицу



Упражнение

Ответ см. в приложении А.

1. Напишите программу, которая создает таблицу соответствия идентифи-каторов пользователей и реальних имен из записей файла паролей, а затем с помощью зтой таблицы выводит список реальних имен, принад-лежащих каждой группе, упомянутой в файле групп. (Включает ли ваш список тех пользователей, у которых в записи файла паролей стоит группа по умолчанию, но в записи файла групп явного упоминания зтой группы нет? Если не включает, как зто сделать?)

Напишите программу, которая вычисляет длину



Упражнения

Ответы к упражнениям приведены в приложении А.
1. Напишите программу, которая вычисляет длину окружности с радиусом 12, 5. Длина окружности равна 2я (около 2 * 3,141592654) радиусам.

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

4. Напишите программу, которая считывает строку и число и выводит на экран строку столько раз, сколько задано числом, причем каждый раз с новой строки. (Совет: используйте оператор х.)

Напишите программу, которая работает аналогично



Упражнения

Ответы к упражнениям даны в приложении А.
1. Напишите программу, которая работает аналогично команде cat, но изменяет на обратный порядок следования всех строк всех файлов, указанных в командной строке, или всех строк, поступающих со стандартного ввода, если файлы не указаны. (В некоторых системах есть подобная утилита; она называется tac.)
2. Измените программу из предыдущего упражнения таким образом, чтобы в каждом файле, указанном в командной строке, порядок строк изменялся на обратный индивидуально. (Да-да, вы можете сделать это, пользуясь тем материалом, который до сих пор изучили, даже не возвращаясь к обзору возможностей языка Perl, приведенному в главе 1.)
3. Напишите программу, которая читает список строковых значений (каждое из которых занимает отдельную строку) и выводит эти строки в 20-символьном столбце с выравниванием справа. Например, при вводе строк hello и good-bye они выводятся в 20-символьном столбце с выравниванием справа. (Добейтесь, чтобы ваша программа действительно использовала 20-символьный столбец, а не 21-символьный. Это весьма распространенная ошибка.)
4. Измените программу из предыдущего упражнения таким образом, чтобы пользователь мог выбирать ширину столбца. Например, при вводе строк 20, hello и good-bye получатся те же результаты, что и в предыдущем упражнении, а ввод 30, hello, good-bye обеспечит размещение hello и good-bye в 30-символьном столбце.

Напишите программу чтения имени файла



Упражнения

Ответы приведены в приложении А.

1. Напишите программу чтения имени файла из stdin, открытия этого файла и выдачи его содержимого с предварением каждой строки именем файла и двоеточием. Например, если считано имя fred, а файл fred состоял из трех строк, a a a, bbb и с с с, вы должны увидеть fred: а а а, fred: bbb И fred: ссс.

2. Напишите программу, которая приглашает ввести имя входного файла, имя выходного файла, образец поиска и заменяющую строку, после чего копирует входной файл в выходной, заменяя все экземпляры образца этой строкой. Попробуйте выполнить эту программу с несколькими файлами. Можете ли вы заменить существующий файл (не проводите эту операцию над чем-то важным!)? Можете ли вы использовать символы регулярных выражений в искомой строке? Можете ли вы использовать $1 в заменяющей строке?

3. Напишите программу чтения списка имен файлов и выдачи информации о том, какие файлы доступны для чтения, для записи и (или) для выполнения, а какие файлы не существуют. (Каждую проверку можно выполнять для каждого имени файла по мере их чтения или для всей совокупности имен после прочтения их всех. Не забудьте удалять символ новой строки в конце каждого прочитанного имени файла.)
4. Напишите программу чтения списка имен файлов и поиска среди них самого старого. Выведите на экран имя файла и его возраст в днях.

UUCP


Если у вас или у вашей организации есть доступ к UUNET, это значит, что у вас должна быть система, имеющая прямое UUCP-соединение с этой сетью. Найдите эту систему и введите (в одну строку):

uucp uunet\ !~/published/oreillу/nutshell/learning_perl2/examples.tar.gz ваш_хост\!~/ваше_имя/

    Если вместо csh-shell вы пользуетесь Bourne-shell (sh), обратные косые можно опустить. Файл примеров должен появиться через некоторое время (день или более) в каталоге /usr/spool/uucppublic/ваше_имя.

Man-страница Тема
perlref Ссылки
perldsc Введение в структуры данных
perllol Структуры данных: списки списков
perltoot Пособие по объектно-ориентированному программированию
perlobj Объекты
perltie Объекты, скрытые за простыми переменными
perlbot Хитрости и примеры использования объектов
pertipc Межпроцессное взаимодействие
perldebug Отладка
perldiag Диагностические сообщения
perlsec Безопасность
perltrap Ловушки для неосторожных
perlstyle Руководство по стилю
perlpod Старая документация в виде простого текста
perlbook Информация о книгах
perlembed Методы встраивания Perl-кода в приложение, написанное на С или C++
perlapio Внутренний интерфейс абстрагирования ввода-вывода
perlxs Интерфейс прикладного программирования XS
perlxsfut Пособие по XS
perlgufs Внутренние функции для тех, кто разрабатывает расширения
perlcall Правила вызова из С








Ваша CGIпрограмма в контексте



Ваша CGI-программа в контексте

На Рисунок 19.1 показаны взаимосвязи между Web-броузером, Web-сервером и CGI-программой. Когда вы, работая со своим броузером, щелкаете на какой-либо ссылке, помните, что с зтой ссьшкой связан универсальный локатор ресурса, URL (Uniform Resource Locator). Зтот URL указывает на Web-сервер и ресурс, доступний через данини сервер. Таким образом, броузер взаимодействует с сервером, запрашивая указаними ресурс. Если, скажем, ресурс представляет собой HTML-форму, предназначенную для заполнения, то Web-сервер загружает зту форму в броузер, который затем выводит ее на зкран, чтобы вы могли ввести требуемне данные.

Каждое предназначенное для ввода текста поле в зтой форме имеет имя (указанное в HTML-коде формы) и соответствующее значение, которым является все, что вы вводите в зтом поле. Сама форма связана (через HTML-директиву <form>) с CGI-программой, которая обрабатывает данные, введенные в форму. Когда вы, заполнив форму, щелкаете на кнопке Submit, броузер обращается к URL CGI-программы. Перед зтим он добав-ляет в конец URL так называемую строку запроса, которая состоит из одной или более пар имя=з наче ниє; каждое имя — зто имя поля, предназначенного для ввода текста, а каждое значение — данные, которые вы ввели. Таким образом, URL, в который броузер передает данные, введенные вами в форму, выглядит приблизительно так (строкой запроса считается все, что стоит после вопросительного знака):

http://www.SOMEWHERE.org/cgi-bin/some_cgi_prog?flavor=vanilla&size=double

Вы видите здесь две пары имя=значение. Такие пары отделяются друг от друга амперсандом (&). Работая с модулем CGI.pm, об зтой детали можете не беспокоиться. Компонент /cgi-bm/some_cgi_prog/WA рассмотрим немного позд-нее; на данный момент важно лишь то, что он содержит путь к CGI-программе, которая будет обрабатывать данные, введенные в HTML-форму.

* Pod сокращенно обозначает plain old documentation ("обычная старая документация"). Зто упрощенная форма представлення, испояьзуемая для всей документации на Perl. Принцип работы зтого формата изложен на man-странице perlpod(l), а некоторыс pod-трансляторы описанм на man-страницах pod2man(l), pod2html(l) и pod2text(l).

Рисунок 19.1. Заполнение форми с привлечением CGI

Когда Web-сервер (в данном случае www.SOMEWHERE.org) получает URL от вашего броузера, он вызывает указанную CGI-программу и передает в нее в качестве аргументов пары имя=значение. Программа затем делает то, что должна делать, и (как правило) возвращает HTML-код серверу, который, в свою очередь, загружает его в броузер для представлення пользователю.

"Переговоры" между броузером и сервером, а также между сервером и CGI-программой ведутся в соответствии с протоколом, известным как HTTP. При написаний CGI-программы об зтом беспокоиться не нужно, т.к. модуль CGI.pm сам решает все вопросы, связанные с использованием протокола.

Способ получения CGI-программой ее аргументов (й другой информа-ции) от броузера через сервер описывается спецификацией Common Gateway Interface. Об зтом тоже не нужно беспокоиться; как вы вскоре увидите, CGI.pm автоматически обрабатывает зти аргументы, не требуя от вас каких-либо дополнительных действий.

Наконец, вам следует знать, что CGI-программы могут работать с любым HTML-документом, а не только с формой. Например, вы могли бы написать такой HTML-код:

Click <a href="http://www.SOMEWHERE.org/cgi-bin/fortune.cgi">here</a> to receive your fortune.

Зцесь fortune.cgi —

программа, которая просто вызывает программу fortune (в UNIX-системах). В данном случае в CGI-программу не вводятся никакие аргументы. Другой вариант: HTML-документ мог бы содержать две ссылки для пользователя — одну для получения предсказания судьбы, вторую для выяснения текущей даты. Обе ссылки могли бы указывать на одну и ту же программу — в одном случае с аргументом fortune, поставленным в URL после вопросительного знака, а в другом случае — с аргументом date. Зти HTML-ссылки выглядели бы так:

<а href="http://www.SOMEWHERE.org/agi-bin/fortune_or_date?fortune"> <a href="http://www.SOMEWHERE.org/cgi-bin/fortune_or_date?date">

CGI-программа (в данном случае fortune_or_date) определила бы, какой из двух возможных аргументов получен, и вьшолнила бы соответственно программу fortune или программу date.

Как видите, аргументи вовсе не должны иметь формат имя=значение, характерний для HTML-форм. Вы можете написать CGI-программу, которая будет делать практически все, что вы пожелаете, и можете передавать ей практически любые аргументы.

В зтой главе мы будем, главным образом, рассматривать применение HTML-форм. При зтом мы предполагаем, что вы уже знакомы с простими HTML-кодами.



Возвращаемые значения Вызов подпрограммы


Давайте, например, определим такую подпрограмму:

sub sum_of_a and_b ( return $a + $b;

}

Последнее выражение, вычисляемое в теле этой подпрограммы (фактически единственное вычисляемое выражение), — сумма переменных $а и $ь, поэтому эта сумма и будет возвращаемым значением. Вот как все это работает:

$а = 3; $b = 4;

$с = sum_of_a_and_b(); # $с присваивается значение 7 $d = 3 * sum_of_a_and_b(); # $d содержит значение 21

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

sub Ii st_o f_a_and_b {

return($a,$b) ;

}

$a = 5; $b = 6;

@c = list_of_a_and_b(); # @c присваивается значение (5,6)

Последнее вычисленное выражение действительно означает последнее вычисленное выражение, а не последнее выражение, определенное в теле подпрограммы. Например, следующая подпрограмма возвращает $а, если $а > 0; в противном случае она возвращает $Ь:

sub gimme a_or_b ( if ($a > 0) (

print "choosing a ($a)\n";

returns $a;

) else (

print "choosing b ($b)\n";

returns $b;

} }

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



Встраиваемость и расширяемость



Встраиваемость и расширяемость

"Внутренности" Perl определены достаточно хорошо для того, чтобы встраивание компилятора-интерпретатора Perl в другое приложение (так, как это уже сделано с Web-сервером Apache и текстовым редактором vi) или расширение Perl путем добавления к нему произвольного кода, написанного на С (или имеющего С-подобный интерфейс), стало относительно несложной задачей. Более того, почти третья часть диалоговой документации на Perl посвящена именно вопросам встраивания и расширения этого языка. Подробно эти аспекты освещены на man-страницах perlembed(l), perlapio(l), perlxs( 1), perlxstut( 1), perlguts( 1) и perlcall( 1).

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

Вопросы безопасности

Perl создавался с учетом требований безопасности. Посмотрите в главе 6 книги Programming Perl или на man-странице perlsec(l) материал о "проверке пороков" (taint checking). "Проверка пороков" — это такой вид защиты, когда вы доверяете автору программы, а не лицу, которое ее выполняет — как это часто бывает в UNIX с программами, в которых применяется идентификатор пользователя, и во всех системах с программами, запускаемыми сервером. Модуль Safe, который описан на man-странице Safe(3) и в главе 7 книги Programming Perl, обеспечивает нечто совершенно иное — защиту, необходимую при выполнении (как в случае с eval) непроверенного кода.

Операторы swithch и case

Нет, таких операторов в Perl нет, но их легко создать с помощью базовых конструкций. См. главу 2 книги Programming Рег1и man-страницу perlfunc(\).

Прямой ввод-вывод: sysopen, sysread, syswrite, sysseek

Иногда средства ввода-вывода Perl оказываются слишком высокоуровневыми для стоящей перед вами задачи. В главе 3 книги Programming Perl и на man-странице perlfunc(l) рассматривается вопрос непосредственного использования базовых системных вызовов для ввода-вывода.

Компилятор Perl

Хотя мы говорим, что Perl компилирует ваш код перед тем, как его выполнять, эта скомпилированная форма не является "родным" объектным кодом. Компилятор Perl, написанный Малькольмом Бити, может создать из вашего Perl-сценария независимый байтовый код или компилируемый С-код. Ожидается, что в версию Perl 5.005 будет включена возможность генерации собственного кода. Подробности см. в материале, представленном на man-странице perlfaq(3).

Поддержка баз данных

Да, Perl может обеспечить непосредственное взаимодействие с коммерческими серверами баз данных, включая Oracle, Sybase, Informix и ODBC, не считая многих других. Соответствующие модули расширения вы найдете в разделе баз данных в каталоге модулей CPAN.

Сложные структуры данных

Используя ссылки, вы можете создавать структуры данных произвольной степени сложности. Вопросы их разработки рассматриваются в главе 4 книги Programming Perl и на man-страницах perllol(l), perldsc(l) и perlref(l). Если вы предпочитаете объектно-ориентированную структуру данных, обратитесь к главе 5 вышеупомянутой книги и man-страницам perltoot(\) и perlobj(l).

Указатели на функции

Perl может сохранять и передавать указатели на функции посредством записи вида \&funcname, а также вызывать их косвенно посредством записи &$funcptr ($args). Можно даже писать функции, которые создают и возвращают новые анонимные функции, как в языках Lisp и Scheme. Такие анонимные функции часто называют замыканиями (closures). Подробности см. в главе 2 книги Programming Perl и на man-страницах perlsub(l) и perlfaq7(l).



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



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

Слово Perl является аббревиатурой выражения Practical Extraction and Report Language (практический язык извлечений и отчетов), хотя иногда его называют Pathologically Eclectic Rubbish Lister (патологически эклектичный мусорный листер). Не стоит спорить о том, какое из этих названий более правильное, потому что оба они принадлежат Ларри Уоллу, создателю и главному архитектору, распространителю и опекуну языка Perl. Ларри создал этот язык, когда пытался формировать отчеты из иерархии файлов системы оповещения об ошибках, похожей на Usenet-новости, а возможности применявшегося в то время обработчика потоков данных awk оказались исчерпанными. Будучи настоящим (то есть ленивым) программистом, Ларри решил вырвать данную проблему с корнем, применив для этого какой-нибудь универсальный инструмент, который он надеялся использовать и в дальнейшем. В результате появилась первая версия языка Perl.

Позабавившись немного с этой версией, добавив кое-что, Ларри предложил ее сообществу читателей материалов телеконференций Usenet, известному также как "Сеть" (the Net). Пользователи, имеющие доступ к входящим в систему Usenet компьютерам, разбросанным по всему свету (а их в то время было несколько десятков тысяч), обеспечили для создателя Perl эффективную "обратную связь", спрашивая, как делать одно, другое, третье. Многие из этих задач Ларри даже и не собирался ставить перед своим маленьким новым языком программирования.

В результате Perl все рос и рос, причем почти с той же скоростью, что и операционная система UNIX. (Специально для новичков: все ядро UNIX тогда требовало памяти объемом 32 К! Теперь мы счастливы, если нам удается уместить его в несколько мегабайтов.) Выросли и его возможности.

Повысилась переносимость. То, что когда-то было компактным языком, теперь сопровождается сотнями страниц документации, в состав которой входят десятки man-страниц, 600-страничный справочник серии Nutshell, материалы множества телеконференций Usenet с 200000 подписчиков, — а теперь еще и эта скромная книга.

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

Примеры программ, содержащихся в этой книге, мы проверяли на Perl версии 5.004 (на момент написания книги это был самый последний выпуск). Все программы, которые здесь рассматриваются, должны работать с Perl версии 5.0 и со всеми последующими версиями. По сути дела, даже программы, написанные на языке Perl версии 1.0, довольно неплохо работают с последними версиями, если только не сталкиваются с некоторыми эксцентричными нововведениями, сделанными во имя прогресса.



Ввод из операции "ромб"



Ввод из операции "ромб"

Другой способ чтения входной информации состоит в использовании операции "ромб" (О). По принципу работы она похожа на <stdin> — тем, что возвращает в скалярном контексте одну строку (или undef, если все строки прочитаны), а в списочном контексте — все оставшиеся строки. В отличие, однако, от операции <stdin> операция "ромб" получает данные из файла или файлов, заданных в командной строке при вызове Perl-программы. Пусть, например, у вас есть программа kitty:

#!/usr/bin/peri while (<>) ( print $_;

i

и вы вызываете ее так:

kitty filel file2 file3

Операция "ромб" читает сначала все строки первого файла, затем все строки второго и, наконец, все строки третьего файла. Значение undef возвращается только тогда, когда будут прочитаны все строки всех файлов. Как видите, программа kitty немного похожа на UNIX-команду cat — тем, что посылает все строки указанных файлов по очереди на стандартный вывод. Если, как в cat, имена файлов в командной строке не указаны, операция "ромб" автоматически читает данные со стандартного ввода.

В ходе своего выполнения операция "ромб" не рассматривает аргументы командной строки непосредственно, а работает с массивом @argv. Это специальный массив, инициализируемый интерпретатором Perl в соответствии с аргументами командной строки. Каждый аргумент заносится в отдельный элемент этого массива. Интерпретировать этот массив можно как угодно*. Можно даже определить этот массив в своей программе и заставить операцию "ромб" работать с этим новым списком, а не с аргументами командной строки, например:

@ARGV = ("aaa","bbb","ccc");

while (о) { # обработать файлы aaa, bbb и ссс print "this line is: $_";

t

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



Ввод из STDIN



Ввод из STDIN

Чтение со стандартного ввода (через Perl-дескриптор файла stdin) — несложная задача. Мы уже делали это в операции <stdin>. Выполняя эту операцию в скалярном контексте, мы получаем следующую строку ввода* (а если строк больше нет — то значение undef):

$а = <STDIN>; # прочитать следующую строку

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

@а = <STDIN>;

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

while (defined($line_ = <STDIN>)) (

# здесь обработать $line )

Пока есть непрочитанные строки, в результате выполнения операции <stdin> получается определенное значение, и выполнение цикла продолжается. Если строк для чтения у <stdin> больше нет, эта операция возвращает значение undef и завершает цикл.

* До символа новой строки или того, что вы присвоили переменной $/.

Операция чтения скалярного значения из <stdin> в $_ и использование этого значения в качестве переменной цикла (как в предыдущем примере) выполняется довольно часто, поэтому в Perl предусмотрена для этого случая специальная сокращенная запись. Если в выражении для проверки цикла указан только оператор чтения со стандартного ввода (нечто вроде <...>), то Perl автоматически копирует строку, которая считывается, в переменную $_.

while (<STDIN>) { # как "while(defined($_ = <STDIN>_) "

chomp; # как "chomp($_)" # здесь выполняются другие операции с $_ )

Поскольку переменная $_ по умолчанию используется во многих операциях, таким способом вы можете значительно сократить объем набираемого текста.



Вывод в STDOUT



Вывод в STDOUT

Для посылки каких-либо данных на стандартный вывод в Perl служат функции print и printf. Давайте посмотрим, как они работают.

Функция print — обычный вывод

Мы уже пользовались функцией print, направляя текст на стандартный вывод. Давайте остановимся на этом вопросе несколько подробнее.

Функция print получает список строк и посылает их по очереди на стандартный вывод, не добавляя никаких промежуточных или конечных символов. Возможно, не совсем очевидно, что print — это просто функция, которая принимает список аргументов и возвращает определенное значение подобно любой другой функции. Другими словами,

$а = print("hello ", "world", "\n");

— это еще один вариант вывода приветствия hello, world. Возвращаемое значение функции print — "истина" или "ложь", говорящее соответственно об успешном и неудачном выполнении. Она почти всегда выполняется успешно, если только не произошла ошибка ввода-вывода, поэтому в рассматриваемом случае $а обычно бывает равно 1.

Иногда в print нужно вводить круглые скобки, как показано в данном примере, особенно когда первый элемент, который вы хотите вывести, сам начинается с левой скобки:

print (2+3),"hello"; # неверно! Выводит 5, игнорируя "hello" print ((2+3),"hello"); # верно, выводит Shello print 2+3,"hello"; # тоже верно, выводит Shello

* Стандартный дистрибутив Perl содержит модули getopt-подобного синтаксического анализа аргументов командной строки Perl-программы. Информация об этой библиотеке есть в книге Programming Perl и на man-странице perlmodlib(l).

Функция printf — форматированный вывод

Вероятно, вы захотите в большей степени контролировать выводимые данные, чем это позволяет функция print. Возможно, вы привыкли к форматированному выводу С-функции printf. Спешим обрадовать: в Perl есть почти сравнимая операция с таким же именем.

Функция printf принимает список аргументов (заключенный в необязательные круглые скобки, как и в случае использования функции print). Первый аргумент — строка управления форматом, указывающая, как выводить остальные аргументы. Если вы не знакомы со стандартной функцией printf, обратитесь к man-странице printf(3) или perlfunc{\), если она у вас есть, или к главе 3 книги Programming Perl.

Пример:

printf "%15s %5d %10.2f\n", $s, $n, $r;

Эта функция выводит $s в 15-символьном поле, затем пробел, затем $п как десятичное целое в 5-символьном поле, затем еще один пробел, затем $г как значение с плавающей запятой с двумя десятичными знаками в 10-символьном поле и, наконец, символ новой строки.



Вызов формата Вызов формата производится


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

format ADDRESSLABEL =

$name

$address

$city, $state, $zip

open(ADDRESSLABEL,">labels-to-print") || die "can't create";

open (ADDRESSES,"addresses") || die "cannot open addresses";

while (<ADDRESSES>) f

chomp; # удалить символ новой строки

($name,$address,$city,$state,$zip) = split (/:/);

# загрузить глобальные переменные

write (ADDRESSLABEL); # send the output }

Здесь мы видим уже знакомое определение формата, но теперь у нас есть и выполняемый код. Сначала мы открываем дескриптор для выходного файла, который называется labels-to-print. Отметим, что имя дескриптора файла (addresslabel) совпадает с именем формата. Это важно. Затем мы открываем дескриптор для файла, содержащего список адресов. Предполагается, что этот список имеет следующий формат:

Stonehenge:4470 SW Hall Suite 107 :Beaverton:OR:97005 Fred Flintstone:3737 Hard Rock Lane:Bedrock:OZ:999bc

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

Цикл while в программе считывает строку из файла адресов, избавляется от символа новой строки, а затем разбивает то, что осталось, на пять переменных. Обратите внимание: имена переменных — те же самые, которые мы использовали, определяя формат. Это тоже важно.

После загрузки значений всех необходимых для работы с форматом переменных функция write вызывает формат. Отметим, что параметром функции write является дескриптор файла, в который будет осуществляться запись; кроме того, по умолчанию используется формат с тем же именем.

Каждое поле в формате заменяется соответствующим значением из следующей строки формата. После обработки двух приведенных в примере записей файл labels-to-print будет содержать следующее:

Stonehenge

4470 SW Hall Suite 107

Beaverton , OR 97005

Fred Flintstone 3737 Hard Rock Lane Bedrock , OZ 999bc



Вызов пользовательской функции


say_hello(); # простое выражение

$а = 3 + say_hello() # часть более сложного выражения

for ($х = start_value(); $х < end_value(); $х += increment О) (

} t

вызов трех подпрограмм для определения значений

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

** Если только вы не выполняете программу с ключом -w.

Одна подпрограмма может вызывать другую подпрограмму, которая, в свою очередь, может вызывать третью подпрограмму и т.д., пока вся наличная память не будет заполнена адресами возврата и не полностью вычисленными выражениями. (Ведь настоящего программиста вряд ли удовлетворят какие-то 8 или 32 уровня вложенности подпрограмм.)