Хранение файловых манипуляторов в переменных
Проблема
Вы собираетесь использовать файловый манипулятор как обычную переменную, чтобы его можно
было передать или вернуть из функции, сохранить в структуре данных и т. д.
Решение
Если у вас уже имеется символьный файловый манипулятор (например, STDIN или LOGFILE),
воспользуйтесь записью тип-глоба, *FH. Такой подход является самым эффективным.
$variable = *FILEHANDLE # Сохранить в переменной;
subroutine(*FILEHANDLE) # или передать функции;
sub subroutine {
my $fh = shift;
print $fh "Hello, filehandle! \n";
}
Если вы хотите работать с анонимным файловым манипулятором, воспользуйтесь функцией return_fh
(см. ниже) или новыми методами модулей IO::File или IO::Handle, сохраните его в скалярной переменной и
используйте так, словно это обычный файловый манипулятор:
use FileHandle # Анонимные манипуляторы;
$fh = FileHandle->new();
use IO::File # 5.004 и выше;
$fh = IO::File->new();
Комментарий
Существует немало способов передать файловый манипулятор функции или сохранить его в структуре
данных. Самое простое и быстрое решение заключается в применении тип-глоба, *FH. Рассматривайте
запись *FH как обозначение типа файлового манипулятора, подобно тому, как представляли молекулы
на уроках химии в виде цветных шариков — не совсем точно, зато удобно. Когда вы начнете понимать
недостатки этой модели, она вам уже не понадобится.
Конечно, в простых ситуациях этого вполне достаточно, но что если вам потребовался массив
файловых манипуляторов с неизвестными именами? Построение анонимных массивов, хэшей и даже функций
во время выполнения программы оказывается исключительно удобным приемом. Нам хотелось бы иметь
аналогичную возможность и для файловых манипуляторов. На помощь приходят модули IO.
Метод new модуля IO::Handle или IO::File генерирует анонимный файловый манипулятор.
Его можно передать функции, сохранить в массиве и вообще применять везде, где используются
именованные тип-глобы файловых манипуляторов — и не только. Эти модули также могут использоваться
в иерархии наследования, поскольку конструктор new возвращает полноценные объекты, для которых
могут вызываться методы.
Объекты могут косвенно использоваться в качестве файловых манипуляторов, что избавляет вас
от необходимости придумывать для них имена.
Чтобы получить тип-глоб из именованного файлового манипулятора, снабдите его префиксом *:
$fh_a = IO::File->new("< /etc/motd") or die "open /etc/motd: $!";
$fh_b = *STDIN;
some_sub($fh_a, $fh_b);
Существуют и другие способы, но этот проще и удобнее всех остальных. Единственное
ограничение — в том, что его нельзя превратить в объект вызовом bless. Bless вызывается для
ссылки на тип-глоб — именно это и происходит в IO::Handle. Ссылки на тип-глоб, как и сами
тип-глобы, можно косвенно использовать в качестве файловых манипуляторов, с приведением
посредством bless или без него.
Создание и возврат нового файлового манипулятора из функции происходит следующим образом:
sub return_fh { # Создание анонимных файловых манипуляторов
local *FH; # Должны быть local, не my
# now open it if you want to, then ...
return *FH;
}
$handle = return_fh();
Функция, получающая файловый манипулятор в качестве аргумента, может либо сохранить
его в переменной (желательно лексической) и затем косвенно использовать его:
sub accept_fh {
my $fh = shift;
print $fh "Sending to indirect filehandle\n";
}
либо локализовать тип-глоб и использовать файловый манипулятор напрямую:
sub accept_fh {
local *FH = shift;
print FH "Sending to localized filehandle\n";
}
Оба варианта работают как с объектами IO::Handle, так и с тип-глобами и настоящими файловыми
манипуляторами:
accept_fh(*STDOUT);
accept_fh($handle);
Perl позволяет использовать строки, тип-глобы и ссылки на тип-глобы в качестве косвенных
файловых манипуляторов, но без передачи тип-глобов или объектов IO::Handle можно нарваться на
неприятности. Применение строк ("LOGFILE"; вместо *LOGFILE) между пакетами потребует специальных
усилий, а функции не могут возвращать ссылки на тип-глобы.
В предыдущих примерах файловый манипулятор перед использованием присваивался скалярной
переменной. Дело в том, что во встроенных функциях (print или printf) или в операторе <>
могут использоваться только простые скалярные переменные, но не выражения или элементы хэшей и
массивов. Следующие строки даже не пройдут компиляцию:
@fd = (*STDIN, *STDOUT, *STDERR);
print $fd[1] "Type it: "; # НЕВЕРНО
$got = <$fd[0]> # НЕВЕРНО
print $fd[2] "What was that: $got"; # НЕВЕРНО
В print и printf это ограничение удается обойти — воспользуйтесь блоком и выражением, в
котором находится файловый манипулятор:
print { $fd[1] } "funny stuff\n";
printf { $fd[1] } "Pity the poor %x.\n", 3_735_928_559;
Pity the poor deadbeef.
Внутри блока может находиться и более сложный код. Следующий фрагмент отправляет сообщение
в один из двух адресов:
$ok = -х "/bin/cat";
print { $ok ? $fd[1] : $fd[2] } "cat stat $ok\n";
print { $fd[ 1 + ($ok || 0) ] } "cat stat $ok\n";
Подход, при котором print и printf интерпретируются как вызовы методов: объекта, не работает
для оператора <>, поскольку это настоящий оператор, а не вызов функции с аргументом без запятых.
Если тип-глобы сохранены в структуре, как это было сделано выше, то для чтения записей можно
воспользоваться встроенной функцией readline, работающей аналогично <>:
$got = readline($fd[0]);
См. также
Proverte kod v komentariyah gde pro list tam oshibki detskie
Оставить комментарий:
|
|