Хранение файловых манипуляторов в переменных

Проблема

Вы собираетесь использовать файловый манипулятор как обычную переменную, чтобы его можно было передать или вернуть из функции, сохранить в структуре данных и т. д.

Решение

Если у вас уже имеется символьный файловый манипулятор (например, 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]);

См. также




2013-09-10 17:05:19

Proverte kod v komentariyah gde pro list tam oshibki detskie




Оставить комментарий:
Ваше Имя:
Email:
Антибот: *  
Ваш комментарий: