Время от времени нам нужно создать строковую переменную, которая занимает несколько строк. Как и всегда в 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;
}