Here-документы, или как создать многостроковую переменную в Perl
Время от времени нам нужно создать строковую переменную, которая занимает несколько строк. Как и всегда в Perl, для этого есть несколько способов. Одно из типичных решений - использование here-документов.
Here-document позволяет создать строковую переменную, занимающую несколько строк и сохранить пробелы и переводы строк. Если запустить следующий код, он выведет в точности то, что мы видим от слова "Дорогой" и до строки перед вторым END_MESSAGE.
Неинетрполируемый here-документ
#!/usr/bin/perl use strict; use warnings; my $name = 'Foo'; my $message = <<'END_MESSAGE'; Дорогой $name, я собираюсь отправить тебе это сообщение. с наилучшими пожеланиями Perl Maven END_MESSAGE print $message;
Вывод:
Дорогой $name, я собираюсь отправить тебе это сообщение. с наилучшими пожеланиями Perl Maven
Here-документ начинается с двух знаков "меньше" << и произвольной строки, которая будет указателем окончания here-документа, и точки с запятой ;, указывающей конец выражения. Что довольно странно, ведь выражение на этом не заканчивается. Содержание here-документа только как раз только начинается со следующей строки (в нашем случае со слова "Дорогой") и продолжается до того места, где perl найдет наш произвольный указатель окончания. В данном случае строку END_MESSAGE.
Если вы уже видели here-документы в коде, возможно, вас удивили одиночные кавычки вокруг первого END_MESSAGE. Скорее всего, если вы встречали примеры here-документов в интернете или в корпоративных сетях, там открывающая часть была без кавычек, вроде этого:
my $message = <<END_MESSAGE; ... END_MESSAGE
Это сработает так же, как если бы вы поместили END_MESSAGE в двойные кавычки, как в следующем примере, но это устаревший синтаксис и он перестанет работать начиная с версии 5.20. Не делайте так! Не используйте here-документы без кавычек вокруг определения указателя окончания.
my $message = <<"END_MESSAGE"; ... END_MESSAGE
Если вам уже знакома разница между строками в одиночных и двойных кавычках в Perl, то, наверное, вы не удивитесь тому, что here-документы работают точно так же. Единственная разница в том, что кавычки ставятся вокрг указателя окончания, а не вокруг самой строки. Если кавычек нет, Perl по умолчанию понимает такую строку так, как если бы она была в двойных кавычках. If you already know the
Если вы вернетесь к первому примеру, вы заметите в нашем here-документе переменную $name, которая осталась в таком же виде в выводе. Это потому, что Perl не пытался заменить $name содержимым этой переменной. (Мы могли бы и не объявлять эту переменную в коде. Вы можете попробовать запустить этот скрипт, убрав строку с my $name = 'Foo';.)
Интерполяция в here-документе
В следующем примере мы используем двойные кавычки вокруг указателя окончания, и таким образом переменная $name будет интерполирована:
use strict; use warnings; my $name = 'Foo'; my $message = <<"END_MSG"; Привет $name, Как дела? END_MSG print $message;
Результат выполнения этого скрипта:
Привет Foo, как дела?
Внимание: точное повторение указателя окончания в конце
Обратите внимание. Необходимо убедиться, что указатель окончания должен быть скопирован в конце текста в точности как в начале. Никаких пробелов в начале или в конце. Иначе Perl не распознает его и будет думать, что это продолжение here-документа. Это в том числе значит, что нельзя ставить отступы перед указателем окончания, чтобы они соответствовали отступам в остальном коде. Или можно?
Here-документы и отступы
Если нам нужен here-документ в месте, где должны быть отступы перед кодом, это порождает две проблемы:
#!/usr/bin/perl use strict; use warnings; my $name = 'Foo'; my $send = 1; if ($send) { my $message = <<"END_MESSAGE"; Дорогой $name, я собираюсь отправить тебе это сообщение. с наилучшими пожеланиями Perl Maven END_MESSAGE print $message; }
Первая состоит в том, что, как уже упоминалось, указатель окончания строки должен быть в точности одинаковым в начале и в конце документа, так что в конце перед ним не может быть отступов.
Вторая проблема в том, что в выводе мы увидим все эти пробелы перед каждой строкой:
Дорогой Foo, я собираюсь отправить тебе это сообщение. с наилучшими пожеланиями Perl Maven
Недостаток отступов перед указателем окончания может быть решен, если мы сразу включим их при его объявлении в начале документа: (здесь я использую 4 пробела, так как табы плохо смотрятся в статье, но в коде их можно использовать, если вы принадлежите к лагерю сторонников отступов-с-помощью-табов)
my $message = <<" END_MESSAGE"; ... END_MESSAGE
Лишние отступы в самом тексте можно убрать с помощью подстановки при присваивании.
(my $message = <<" END_MESSAGE") =~ s/^ {8}//gm; ... END_MESSAGE
В подстановке мы заменяем 8 предстоящих пробелов на пустую строку. Мы ипользуем два модификатора: /m меняет поведение ^ таким образом, чтобы он соответствовал не началу строковой переменной, а началу каждой строки. /g указывает, что подстановка должна работать глобально, то есть повторяться до тех пор, пока возможно.
С этими двумя флагами подстановка уберем 8 предстоящих пробелов из каждой строки в переменной слева от =~. Необходимо заключить в скобки выражение слева от оператора, так как приоритет оператора присваивания (=) ниже, чем у оператора регулярного выражения =~. Без скобок perl бы сначала попытался применить регуляроне выражение к here-документу, что закончилось бы ошибкой на этапе компиляции:
Can't modify scalar in substitution (s///) at programming.pl line 9, near "s/^ {8}//gm;"
Использование q или qq вместо here-документов
После всех этих объяснений я уже не уверен, что мне стоит советовать вам использовать here-документы. Во многих случаях вместо них я использую оператор qq или q, в зависимости от того, нужно ли интерполировать переменные:
#!/usr/bin/perl use strict; use warnings; my $name = 'Foo'; my $send = 1; if ($send) { (my $message = qq{ Дорогой $name, я хочу отправить тебе это сообщение. с наилучшими пожеланиями Perl Maven }) =~ s/^ {8}//mg; print $message; }
Published on 2013-09-09