Изменение в форматах установок по умолчанию
Мы часто говорим об использовании в тех или иных ситуациях значений "по умолчанию". В Perl имеется способ отмены этих "умолчаний" практически для всех случаев. Давайте поговорим об этом.
Изменение дескриптора файла с помощью функции select()
Когда мы говорили о функции print в главе 6, я упомянул, что print и print stdout идентичны, потому что stdout — это установка по умолчанию для print. Это не совсем так. Настоящая установка по умолчанию для print (а также для write и еще нескольких операций, до которых мы скоро доберемся) — это выбранный в текущий момент дескриптор файла.
Вначале выбранный в текущий момент дескриптор файла — это stdout, благодаря чему упрощается отправка данных на стандартный вывод. Изменить выбранный в текущий момент дескриптор файла можно с помощью функции select. В качестве аргумента эта функция принимает отдельный дескриптор файла (или скалярную переменную, которая содержит имя дескриптора файла). Изменение выбранного в текущий момент дескриптора файла влияет на все последующие операции, которые от него зависят. Например:
print "hello world\n"; # аналогично print STDOUT "hello worldVn"
select (LOGFILE); # выбрать новый дескриптор файла
print "howdy, world\n"; # аналогично print LOGFILE "howdy, world\n"
print "more for the log\n"; # еще в LOGFILE
select (STDOOT); # вновь выбрать STDOUT
print "back to stdout\n"; # это идет на стандартный вывод
Отметим, что операция select — "липкая"; после выбора нового дескриптора он остается "выбранным в текущий момент" до проведения следующей операции select.
Таким образом, более удачное определение stdout по отношению к функциям print и write будет звучать так: stdout — выбранный в текущий момент дескриптор по умолчанию, или просто дескриптор по умолчанию.
В подпрограммах может возникать необходимость смены выбранного в текущий момент дескриптора файла. Представляете, какое неприятное чувство можно испытать, вызвав подпрограмму и обнаружив, что все тщательно проверенные строки текста уходили куда-то "налево", потому что подпрограмма, оказывается, изменила выбранный в текущий момент дескриптор файла и не восстановила его! Что же должна делать "хорошо воспитанная" подпрограмма? Если она "знает", что текущий дескриптор — stdout, она может восстановить выбранный дескриптор с помощью кода, похожего на приведенный выше. А если программа, которая вызвала подпрограмму, уже изменила выбранный дескриптор файла — что тогда?
Оказывается, значение, возвращаемое функцией select, — это строка, которая содержит имя ранее выбранного дескриптора. Получив данное значение, можно впоследствии восстановить этот дескриптор, используя такой код:
$oldhandle = select LOGFILE;
print "this goes to LOGPILEW;
select ($oldhandle); # восстановить предыдущий дескриптор
Действительно, в приведенных выше примерах гораздо проще в качестве дескриптора файла для print явным образом указать logfile, но некоторые операции, как мы вскоре увидим, требуют именно изменения выбранного в текущий момент дескриптора файла.
Изменение имени формата
Имя формата по умолчанию для конкретного дескриптора файла совпадает с именем этого дескриптора. Для выбранного в текущий момент дескриптора файла этот порядок можно изменить, присвоив новое имя формата специальной переменной $~. Можно также проверить значение этой переменной и посмотреть, каков текущий формат для выбранного в текущий момент дескриптора файла.
Например, чтобы использовать формат addresslabel с дескриптором stdout, следует просто записать:
$~ = "ADDRESSLABEL";
А что, если нужно установить для дескриптора report формат summary? Для этого необходимо сделать всего лишь следующее:
$oldhanlde =• select REPORT;
$~ ° "SUMMARY";
select ($oldhandle);
Когда в следующий раз мы напишем
write (REPORT) ;
то
тем самым передадим текст на дескриптор report, но в формате summary*.
Обратите внимание на то, что вы сохранили предыдущий дескриптор в скалярной переменной, а затем восстановили его. Этот прием — признак хорошего стиля программирования. В коде реальной программы мы, вероятно, решили бы предыдущий однострочный пример таким же способом, а не предполагали бы, что stdout — дескриптор по умолчанию.
Изменяя текущий формат для конкретного дескриптора файла, вы можете чередовать в одном отчете много разных форматов.
Изменение имени формата начала страницы
Точно так же, как путем установки переменной $~ мы можем изменять имя формата для конкретного дескриптора файла, так путем установки переменной $ л мы можем менять формат начала страницы. Эта переменная содержит имя формата начала страницы для выбранного в текущий момент дескриптора файла и доступна для чтения и записи, т.е. вы можете проверить ее значение и узнать текущее имя формата, а также изменить его, присвоив этой переменной новое значение.
* Объектно-ориентированный модуль FileHandle, входящий в состав стандартного дистрибутива Perl, обеспечивает выполнение этой задачи более простым способом.
Изменение длины страницы
Если определен формат начала страницы, длина страницы приобретает особое значение. По умолчанию длина страницы равна 60 строкам, т.е. если результаты работы функции write не умещаются до конца 60-й строки, то перед дальнейшим выводом текста автоматически вызывается формат начала страницы.
Иногда 60 строк — не то, что нужно. Этот параметр можно изменить, установив переменную $=. Данная переменная содержит текущую длину страницы для выбранного в текущий момент дескриптора файла. Опять-таки, для замены дескриптора stdout (выбранного в текущий момент дескриптора файла по умолчанию) на другой нужно использовать операцию select. Вот как следует изменить дескриптор файла logfile так, чтобы страница содержала 30 строк:
$old = select LOGFILE; # выбрать LOGFILE и сохранить старый дескриптор $= ° 30;
select $old;
Изменение длины страницы вступает в силу только при следующем вызове формата начала страницы. Если вы установили новую длину перед выводом текста в дескриптор файла в каком-то формате, то все будет работать как надо, потому что формат начала страницы вызывается при первом же вызове функции write.
Изменение положения на странице
Если вы выводите свой текст в дескриптор файла с помощью функции print, то значение счетчика строк будет неправильным, потому что Perl проводит подсчет строк только для функции write. Если вы хотите дать Perl знать, что выводите несколько дополнительных строк, можно настроить внутренний счетчик строк Perl, изменив значение переменной $-. Эта переменная содержит число строк, оставшихся на текущей странице для выбранного в текущий момент дескриптора файла. Каждая функция write уменьшает число оставшихся строк на число фактически выведенных строк. Когда значение этого счетчика достигает нуля, вызывается формат начала страницы и из переменной $= (задающей длину страницы) копируется значение $-.
Например, чтобы сообщить Perl, что вы послали в STDOUT дополнительную строку, нужно сделать следующее:
write; # вызвать формат STDOUT для STDOUT
print "An extra line... oops!\n"; # это идет в STDOUT $- --; # декрементировать $-, чтобы показать, что в STDOUT пошла строка не из write
write; # сработает, учтя дополнительную строку
В начале программы $- устанавливается в нуль для каждого дескриптора файла. Это позволяет гарантировать, что формат начала страницы будет первым элементом, вызываемым для каждого дескриптора файла при выполнении первой операции write.