Изучаем Perl, часть 4
Автор: © Ben Okopnik
|
Интернет революция была основана на открытых системах; открытая система - это система, на программное обеспечение которой вы можете взглянуть, коробочка, которую вы можете открыть и "поиграть" с её содержимым. Это не секретные бинарные файлы, программы с обрезанной функциональностью или дай-монету-брат shareware. Если бы все всегда прятали программное обеспечение, у вас бы сейчас не было и сотой доли того полезного программного обеспечения, что есть у вас сейчас. Обзор
Если вы читали эту серию статей, у вас должно быть несколько инструментов (
возможно, вы даже экспериментировали с ними), которые можно использовать для
создания скриптов.
Итак, в этом месяце мы посмотрим, как создавать некоторые из них, при помощи
функции "open", которая позволяет назначать файловые указатели для файлов,
сокетов и "труб". "open" - это основной строительный блок в использовании Perl,
итак посмотрим.
Упражнения Последний раз, я упомянул написание нескольких скриптов, для практики. Давайте посмотрим на несколько возможных путей, сделать это. Первым был скрипт, который должен принимать на входе число, и печатать "Hello!" ("Привет!") указанной число раз. Он должен также проверять входное число на наличие "неправильных" (не цифр) знаков. Вот хороший пример, присланный David Zhuwao: #! /usr/bin/perl -wДля начала укажу на пример хорошего стиля программирования : David использовал ключ "-w", чтобы Perl предупреждал его о любых ошибках во время компиляции - это прекрасная привычка. Он также эффективно использовал пустое пространство (пустые линии и табуляции), чтобы сделать код легкочитаемый, а также свободно использовал комментарии. Также, вместо того, чтобы проверять наличие числа (что создало бы проблемы с входными данными вроде "1A"), о проверяет наличие не числовых символов, и не-нулевую длину данных - правильное мышление! К недочетам (заметьте что не один из них не является серьезной проблемой, просто наблюдения): можно отнести использование оператора соответствия (match), "m//", "m" является обязательным , только если разделитель - это символ отличный от "/". Также цикл Perl "for/foreach" будет более компактным, чем в цикл "for" в стиле C, в то же время продолжая выполнять его функцию. print "Hello!\n" for 1 .. $input;
Это также сделает "$i" не нужным. Все остальное мелкие недочеты - отличная работа, David!
Вот другой путь: #!/usr/bin/perl -wВ отличии от версии David'а, моя не печатает сообщение об ошибке; она просто возвращается к запросу, если ввод не цифровой. Также, вместо проверки наличия не-цифровых символов, я проверяю строку от её начала до конца на наличие только цифровых символов. Любая из этих методик будет работать. Также, вместо использования явного цикла, я использую оператор "x", который просто повторяет предшествующую ему команду печати "$a" раз. ...И, Еще Раз... Давайте разберем еще один скрипт, второе предложение предыдущего месяца: скрипт, который берет значение часа (0-23) в качестве входных данных и говорит "Good morning", "Dobriy den" :), "Guten Abend" или "Buenas noches" в качестве результата (сдесь я схитрил и использовал только английский, чтобы избежать путаницы.). #!/usr/bin/perl -wС первого взгляда, этот скрипт кажется достаточно простым - так и есть на самом деле - но он содержит несколько скрытых соображений, о которых я хочу поговорить. Для начала, зачем нам везде проверки на начало и конец строки? Очевидно что мы хотим избежать перепутывания "1" и "12" - но что может быть не так с /1[3-8]/?. Что может случиться, так это опечатка. Не то, чтобы это имело особое значение в данном случае, но быть параноидально настроенным в отношении проверок - это в целом хорошая идея :). Что если пользователь, пытаясь ввести "14", набрал "114"? Без этих "ограничений", это будет соответствовать "11" - и мы получим неверный ответ. ОК - почему я не использую численные проверки вместо проверки на совпадение? Я имею ввиду, что, в конце концов, мы работаем всего лишь с числами.... не будет ли так проще и очевиднее? Да, но, что произойдет если мы проводим численные проверки, а пользователь вводит "joe"? Мы получим ошибку вмести с нашим "Invalid input!" ("Некорректные данные!"): Argument "joe\n" isn't numeric in gt at -e line 5, <> chunk 1.
Это дело хорошего стиля программирования, мы хотим чтобы пользователь видел
только вывод сгенерированный нами (или ожидаемый); не должно быть каких либо
ошибок, вызванных самой программой. Проверка при помощи регулярного выражения,
не будет "удивлена" не числовым вводом; она просто вернет 0 (нет совпадения) и
передаст управление на следующий "elsif" или "else", который будет
ловушкой для ошибки. Все, что не удовлетворяет одному из первых четырех тестов
- это неверный ввод - и мы хотим чтобы об этом было выдано соответствующее сообщение.
Работа с файлами Важная возможность любого языка - это работа с файлами. В Perl, это относительно просто, но есть несколько мест, в которых надо быть осторожными. # Правильный путь Вот несколько неправильных или неоднозначных путей сделать это: # Не проверяет возвращаемый результатПо умолчанию файлы открываются только для чтения. Другие методы могут быть указаны путем добавления довольно очевидного "модификатора" в указанное имя файла:
Помните, что метод открывания файла по умолчаний - это "только для чтения". Я обычно выделяю это, путем записи этого выражения в таком виде: open FILE, "</etc/passwd" or die "Can't open /etc/password: $!\n"; Обратите внимание на знак "<" в начале имени файла: у Perl не возникает с этим проблем, и это является хорошим визуальным напоминанием. Фраза "оставлять хлебные крошки" хорошо описывает эту методологию, и должна восприниматься как наставление делать написанное вами как можно более очевидным, для любого, кто будет использовать код после вас. На забывайте, что эти "кем-то" можете стать вы сами, через несколько лет после того, как вы написали код ... Perl автоматически закрывает указатели файлов, когда скрипт завершает работу ... или по крайней мере должен. Я говорю это потому, что некоторые ОС имеют проблемы с этим - поэтому будет неплохой идеей (хотя и не необходимостью) совершить явную операцию закрытия открытых указателей файлов: close FILE or die "Can't close FILE: $!\n"; Кстати, эффект функции "die" должен быть очевидным: она печатает указанную строку и выходит из программы. Не делайте этого, если вы не на последней строке вашей программы: close; Это закрывает все указатели файлов ... включая STDIN, STDOUT и STDERR (стандартные потоки), что оставляет вашу программу немой, глухой и слепой. Также, вы не можете указывать множество указателей в одной операции close, так что вам придется вместо этого закрывать их по одиночке: close Fh1 or die "Can't close Fh1: $!\n"; Вы конечно можете сделать так: for ( qw/Fh1 Fh2 Fh3 Fh4/ ) { close $_ or die "Can't close $_: $!\n"; }
Вот что такое Perl для вас; Всегда есть более чем один путь сделать что-то ....
Использование указателей Предположим, что у вас есть два файла с финансовой информацией - ставки займов в одном, тип и размер ваших займов в другом - и вы хотите расчитать, сколько процентов вы будете платить, и записать результат в файл. Вот данные: rates.txt loans.txt Итак, давайте приступим: #!/usr/bin/perl -w Объективно, Perl очень хорош в таких вещах: мы проделали необходимую работу в дюжине строк кода. Комментарии занимают большую часть листинга :). Вот другой пример, который явился результатом моей статьи о procmail ( "Нет спаму!" в LG#62. Оригинальный скрипт "черного списка" который вызывался из Mutt, вытаскивал e-mail спаммера при помощи "formail", затем обрабатывал результат вплоть до конкретного "пользователь@хост" адреса при помощи одно-строчного Perl скрипта. Он принимал все письма со спамом как данные входного канала (pipe). Martin Bock, предложил проделывать все это при помощи Perl; после короткой переписки с ним, я создал следующий скрипт, основанный на его идеи: #!/usr/bin/perl -wln Оператор замены, в первой строке, записан не идеально - я могу написать некоторые достаточно "накрученные" e-mail адреса, которые не будут правильно обработаны - но это работает хорошо с вариантами вроде [email protected] Чтобы "расшифровать", что означает регулярное выражение, проконсультируйтесь с man-страницей "perlre". Это не так сложно. :) Подсказка: ищите слово "greed" что понять что значит ".*?", и слово "capture" чтобы понять конструкцию "(...) / $1". Оба из них представляют очень важные концепции, и оба были упомянуты в этой серии статей. Вот более компактная (и поэтому гораздо менее читаемая) версии приведенного выше фрагмента; заметьте, что механизм кое в чем различен: #!/usr/bin/perl -wln Блок BEGIN на первой строке скрипта запускается только 1 раз в ходе исполнения скрипта, несмотря на то, что скрипт циклически исполняется много раз; это очень похоже на аналогичную конструкцию в Awk. В следующий раз В следующем месяце, мы рассмотрим несколько отличных способов избавить себя от лишней работы используя модули: полезный код, который другие люди написали для Comprehensive Perl Archive Network (CPAN). Мы также посмотрим, как можно использовать Perl для GGI, Common Gateway Interface - механизм, который может "рубить лес и носить воду" за кулисами Web. А до того времени вот несколько вещей, с которыми можно "поиграть": Напишите скрипт, который открывает "/etc/services" и подсчитывает сколько портов поддерживает UDP, и сколько TCP. Запишите названия сервисов в файлы называемые "udp.txt" и "tcp.txt", и выведите все на экран. Откройте два файла и обменяйте их содержимое. Считайте "/var/log/messages" и выведите число строк, которые содержат слово "fail",
"terminated/terminating", или " no ". Сделайте его
А сейчас - perl -we 'print "Увидимся в следующем месяце!"'
Ben Okopnik
Man страницы Perl (доступные в любой pro-Perl-y (правильно) сконфигурированной
perl - обзор
perlfaq - Perl FAQ
"perldoc", "perldoc -q" и "perldoc -f"
|
Copyright © 2001, Ben Okopnik. Copying license http://www.linuxgazette.com/copying.html Published in Issue 67 of Linux Gazette, June 2001 |
Вернуться на главную страницу |