В последнее время новичков в Perl мало. Люди не хотят изучать его, потому что сейчас существуют языки, которые значительно проще в изучении. Например, вездесущий php. Но не будем устривать холивар, а остановимся на сути.
Perl имеет некоторые особенности, которые придают ему индивидуальность. Однако, новички часто не замечают этих особенностей. Кроме того, язык Perl более низкоуровневый и гибкий, чем тот же php. Поэтому возникают некоторые проблемы, связанные с понижением уровня при переходе от php к Perl. Какие же ошибки совершают начинающие Perl-хакеры? Одинаковые. Все наступают на одни и те же грабли. Чтобы этих граблей было меньше, я описал и их, и противограблиные методы кодирования. Вот что у меня получилось.
Просто запомните раз и на всегда: Первой строкой в программе на Perl всегда должна быть строка с директивой, указывающей на интерпретатор Perl. Чаще всего эта строка выглядит так:
#!usr/bin/perl
До этой строки не должно идти ничего. Когда я говорю «ничего», я имею ввиду НИЧЕГО, даже пробельные символы и переводы строк.
В Perl оператор точки с запятой не является опциональным, в отличие от, скажем JavaScript. Поэтому символ «;» нужно ставить в конце строки всегда. Исключением может быть последняя строка в блоке:
if($condition) {print $string}
В случае, если в блоке выполняется единственное действие, точка с запятой даже нежелательна, потому что затрудняет визуальное восприятие кода.
В Perl обязательно нужно заключать блоки в фигурные скобки. Даже если блок состоит из одной строки:
for($i = 1..10) print $i #НЕВЕРНО!
for($i = 1..10) {print $i} #Теперь OK
Исключение могут составлять поствиксные операторы. Например:
print $i for ($i = 1..10);
Иногда такая форма записи улучшает читаемость, иногда – нет. Следите за смыслом!
Следующие ошибки связаны с вводом-выводом.
Если вы разрабатываете сайт или веб-приложение, вам понадобится выводить информацию, сгенерированную скриптом в браузер. Чтобы браузер понял что вы такое ему передаете, ему нужно передать верный заголовок с этой информацией. Часто бывает достаточно такого:
print qq(Content-type: text/html\n\n);
Обратите внимание, что этот заголовок должен выводиться ДО контента. Так же, обратите внимание на два спец-символа \n. Поясним, зачем они нужны. Вообще-то, после заголовка должен идти один символ перевода строки. Однако, заголовки должны отделяться от контента пустой строкой или «пустым заголовком». Этот пустой заголовок и описывается одиночным «\n».
То есть, если вы хотите передать не один заголовок, то вы должны указывать один перенос строки в конце каждого заголовка, не являющегося последним, и два – в последнем заголовке:
print qq(Cache-control: no-cahce\n);
print qq(Content-type: text/html; charset=utf-8\n\n);
Здесь мы указываем, что передаваемую страницу не надо кешировать, что передаем мы html-страницу в кодировке utf-8.
Часто встречается ошибка:
print F, $text; #НЕВЕРНО
Со стороны кажется, что все правильно и в файл с дескриптором F должно записаться содержимое $text. На самом деле, этого не произойдет. Виновата запятая после «F». Эта запись должна выглядеть так:
print F $text; #OK
Продолжая тему print и дескриптора файла, можно еще выделить один тип ошибок:
print <F> $text;
Эта запись не верна. Дело в том, что оператор угловых скобок «<DESC>» ЧИТАЕТ из файла с дескриптором DESC одну строку (в скалярном контексте). Для вывода в файл, угловые скобки нужно опустить.
#Для чтения из файла строки, нужно написать следующее:
$line = <F>;
#Для чтения всего файла в массив:
while (<F>) {push @file}
#Для чтения файла в большой скаляр:
while (<F>) {$file .= $_}
#Для записи строки в файл:
print F $text;
Те, кто пришли в Perl из более младших языков, удивятся, почему ошибочный скрипт не выполнится, но не выведет ни единой ошибки в браузер? Просто Perl не проектировался для web. Поэтому все ошибки он отдает серверу, который пишет их в лог ошибок. Вы можете найти сообщения об ошибках в логе сервера. Однако, это не удобно. Гораздо удобнее получать сообщения об ошибках в браузере. Для этого достаточно включить в скрипт строку:
use CGI::Carp qw(fatalsToBrowser);
Теперь все ошибки, возникающие в скрипте, будут перехвачены и выведены в браузер.
Следующие ошибки будут связаны с особенностями синтаксиса Perl.
В Perl переменные снабжаются префиксами, указывающими на их тип. $ - скаляр, @ - массив, % - хеш-массив. Если присмотреться к этим символам, то можно заметить стилизованные буквы «S» и «A» из слов «scalar» и «array», а так же интересное графическое представление ключа и значения в «%». Часто начинающие программисты допускают ошибку, указывая неверный префикс. Например:
@array[$i] #Ошибка!
$array[$i] #OK
%hash{key} #Неверно!
$hash{key} #Теперь нормально.
Если элементом массива является ссылка на другой массив, то нужно добавить еще один префикс, соответствующий типу ссылки. Например, вот столбец из двумерного массива:
@$array[$i] #Получаем i-й элемент @array, который является ссылкой на массив
В Perl многие функции и операторы ведут себя по-разному, в зависимости от контекста, в котором они находятся. Понимание этого механизма приходит с опытом. Однако, вот ошибки, которые встречаются особенно часто.
My $x, $y = ('var1', 'var2'); #Ошибка. My – списковый оператор.
My ($x, $y) = ('var1', 'var2'); #Теперь OK
$x = (1, 2, 3); #Здесь $x == 3
($x) = (1, 2, 3); #А здесь $x == 1
@a = (1, 2, 3, 8);
$x = @a; #$x становится равным 4 – длина массива @a.
Люди, пришедшие из других языков программирования должны смириться с тем, что в Perl оператор повторной проверки на условие пишется elsif.
if ($a)
{...что-то...}
else if ($b) #ОШИБКА!
{...еще что-то...}
Эта конструкция ошибочна, потому что else должен открывать блок. В то же время, следующая запись будет верна.
if ($a)
{...что-то...}
else {if ($b) #Верно, но через ж...
{...еще что-то...}
}
Вам нужно следующее:
if ($a)
{...что-то...}
elsif ($b) #То, что надо!
{...еще что-то...}
Обратите внимание, что в отличие от php, последнюю букву в слове else оператора elsif дописывать не надо. Elsif, а не elseif.
Perl предоставляет удобные системные переменные, которые могут пригодиться для улучшения читабельности кода. Иногда без них вообще не обойтись. Нужно знать как они работают и от чего появляются.
Например, если мы удаляем все пробелы во всех элементах массива и выводим эти элементы, это может выглядеть так:
foreach (@text) {
s/\s//g;
print;
}
Этот код рабочий. Мы присваиваем $_ очередное значение из @text, затем удаляем в нем пробелы, а затем выводим. Спросите, где здесь может быть ошибка? Внесем некоторые изменения. Допустим, наш массив содержит ссылки на масивы. Тогда мы напишем:
foreach (@text) {
foreach (@$_) #Ошибка!
{
s/\s//g;
print;
}
}
От куда же здесь взялась ошибка? Дело в том, что вложенный цикл foreach неявно задает значение переменной-местоимению $_. Сначала происходит обращение к массиву @$_, первое значение из этого массива присваивается переменной $_! Далее, с ней производятся какие-то действия, но это уже не важно. Мы переопределили ту переменную, которая указывала на массив. Решение проблемы лежит на поверхности: запоминайте значения переменных по-умолчанию, когда это нужно.
foreach (@text) {
my $tmp = $_;
foreach (@$tmp) #Теперь ОК
{
s/\s//g;
print;
}
}
Perl обладает парами операторов логического сравнения. Для строк и для чисел. Вы должны четко представлять какой оператор вам нужен в данный момент. Чтобы понять разницу, рассмотрим пример:
«123» == «123.00» #Истинно
«123» eq «123.00» #Ложно
Кроме того, иногда программисты небрежно обращаются с истиной в Perl. Ошибочным может быть такой код:
if ($f) {print $f}
Казалось бы, ну что тут ошибочного? Синтаксически – ничего. Perl не станет ругаться на эту строку. Однако, программист сам мог подложить себе свинью. Дело в том, что если в $f окажется значение «0», то этот ноль не будет выведен, хотя могло подразумеваться иное. Если вы хотите выводить все непустые значения $f, включая ноль, то вам нужно написать:
if ($f ne '') {print $f}
Вот соответствия:
Нужно помнить, что операторы or и and имеют очень низкий приоритет. Иногда, это как раз то, что требуется, но иногда результат, который вы получите будет отличаться от результата, который ожидался. Например, давайте присвоим первое из истинных значений переменной.
$xyz = $x || $y || $z; #$xyz примет первое истинное значение
Но если мы используем or, результат будет другим:
$xyz = $x or $y or $z; #$xyz примет $x, затем будут выполнены or.
Ну вот и все. Вся чертова дюжина в сборе. Это, конечно, не все ошибки, которые возможны. Однако, эти грабли часто встречаются на пути начинающего Perl-кодера. Этой статьей я хотел предостеречь или помочь новичкам в нелегком, но интересном деле освоения Perl.
А какие ошибки знаете вы?
При подготовке материала использовалась книга Ларри Уолла, Тома Кристиансена и Джона Орванта «Программирование на Perl» («Programming Perl»), O'Reily, 2006