«Все потребности в нем заложены, какие только бывают на свете. И все эти потребности он может удовлетворить. С помощью нашей науки, разумеется.»
А. и Б. Стругацкие
Я думаю, многим из Perl-программистов знакома следующая конструкция построчного чтения содержимого файла:
Как вы знаете, цикл
а)
б) пустой строкой;
в) строкой, содержащей единственный символ
Что же происходит, когда в файле вдруг встречается пустая строка? Неужто цикл перестанет работать? Отнюдь. Считываемые строки включают в себя маркер конца строки, а значит, пустая строка на самом деле не пустая, а
Можете не торопиться: код совершенно правильный и работать будет корректно, и связано это с хитрой особенностью оператора
карета превращается в тыкву оператор теряет все свои волшебные качества и начинает проверять условия строго в соответствии с правилами преобразования типов. О чём нам незамедлительно сообщит компилятор Perl, если, конечно, не забыть его попросить об этом ключом
А. и Б. Стругацкие
Я думаю, многим из Perl-программистов знакома следующая конструкция построчного чтения содержимого файла:
while (<FILE>) {
# do something
}
Этот код стал настолько привычным, что многие даже не задумываются, а как же он, собственно, работает. В данной статейке я опишу одну особенность, о которой весьма полезно помнить.Как вы знаете, цикл
while
выполняется, пока его условие является истинным. Однако в нашем случае в качестве условия указана строка, которая даёт булевскую истину только в том случае, если она не является:а)
undef
;б) пустой строкой;
в) строкой, содержащей единственный символ
"0"
.Что же происходит, когда в файле вдруг встречается пустая строка? Неужто цикл перестанет работать? Отнюдь. Считываемые строки включают в себя маркер конца строки, а значит, пустая строка на самом деле не пустая, а
"\n"
, и поэтому будет расценена в булевском контексте как истина. То же самое относится к случаю, когда в файле попадается строчка с одним лишь символом "0"
. Но есть случай, когда это правило не срабатывает: если последняя строчка файла содержит лишь символ "0"
и не завершается символом новой строки. В результате при чтении этой последней строчки мы получим строку "0"
, которая для while
должна считаться ложной, и, следовательно, эта строка не будет обработана. Правильно? Казалось бы, всё правильно, и теперь надо срочно хвататься либо за любимый текстовый редактор, либо за валидол, либо за то и другое одновременно, — в зависимости от степени важности проекта, где использовался данный код.Можете не торопиться: код совершенно правильный и работать будет корректно, и связано это с хитрой особенностью оператора
while
. Цитирую perlop (в вольном переводе):Нижеприведённые строки эквивалентны:Таким образом, внутри цикла
А эта строка кода работает аналогичным образом, но без использования $_:while (defined($_ = <STDIN>)) { print; }
while ($_ = <STDIN>) { print; }
while (<STDIN>) { print; }
for (;<STDIN>;) { print; }
print while defined($_ = <STDIN>);
print while ($_ = <STDIN>);
print while <STDIN>;
Во всех этих конструкциях для присваиваемого значения (независимо от того, явное было присваивание или нет) выполняется проверка на то, определено ли значение. Это позволяет избежать проблемы, когда строковое значение является ложным в булевском контексте, например, "" или "0" без завершающего символа новой строки. Если вы намеренно хотите использовать эти значения для завершения цикла, необходимо проверять их явным образом:while (my $line = <STDIN>) { print $line }
В других выражениях с булевским контекстом при использованииwhile (($_ = <STDIN>) ne '0') { ... }
while (<STDIN>) { last unless $_; ... }<filehandle>
будет выведено предупреждение, если отсутствует явный вызов функцииdefined
или операция сравнения, при условии, что включена прагмаuse warnings
или используется параметр командной строки-w
(или переменная$^W
).
while
чтение из файла автоматически оборачивается в проверку на defined
. Однако стоит вам заменить while
на if
или даже просто усложнить условие цикла, добавив через and
или or
ещё какое-нибудь подвыражение, как -w
.