Доступ к пустому углу Perl “<>” оператор из фактического дескриптора файла?
-
08-07-2019 - |
Вопрос
Мне нравится использовать изящную функцию perl для чтения из оператора empty angle <>
волшебным образом предоставляет вашей программе семантику фильтра UNIX, но я хотел бы иметь возможность получить доступ к этой функции через фактический дескриптор файла (или Ввод-вывод::Ручка object или что-то подобное), чтобы я мог делать такие вещи, как передавать его в подпрограммы и тому подобное.Есть ли какой-нибудь способ сделать это?
Этот вопрос особенно сложно найти в Google, потому что поиск по "оператору угла" и "дескриптору файла" просто подсказывает мне, как читать из дескрипторов файлов с помощью оператора угла.
Решение
От perldoc perlvar
:
ARGV
Специальный дескриптор файла, который перебирает имена файлов командной строки в
@ARGV
.Обычно записывается как нулевой дескриптор файла в операторе angle<>
.Обратите внимание, что в настоящее времяARGV
оказывает свое магическое действие только в пределах<>
оператор;в другом месте это просто дескриптор файла, соответствующий последнему файлу, открытому<>
.В частности, прохождение\*ARGV
в качестве параметра функции, которая ожидает дескриптор файла, может не привести к тому, что ваша функция автоматически прочитает содержимое всех файлов в@ARGV
.
Я полагаю, что это отвечает на все аспекты вашего вопроса в духе "Ненавижу это говорить, но это не даст того, чего вы хотите".Что вы могли бы сделать, так это создать функции, которые открывают список имен файлов, и сделать это:
sub takes_filenames (@) {
local @ARGV = @_;
// do stuff with <>
}
Но это, вероятно, лучшее, что вы сможете сделать.
Другие советы
Развивая идею Криса Латца, приведу очень элементарную реализацию:
#!/usr/bin/perl
package My::ARGV::Reader;
use strict; use warnings;
use autodie;
use IO::Handle;
use overload
'<>' => \&reader,
'""' => \&argv,
'0+' => \&input_line_number,
;
sub new {
my $class = shift;
my $self = {
names => [ @_ ],
handles => [],
current_file => 0,
};
bless $self => $class;
}
sub reader {
my $self = shift;
return scalar <STDIN> unless @{ $self->{names}};
my $line;
while ( 1 ) {
my $current = $self->{current_file};
return if $current >= @{ $self->{names} };
my $fh = $self->{handles}->[$current];
unless ( $fh ) {
$self->{handles}->[$current] = $fh = $self->open_file;
}
if( eof $fh ) {
close $fh;
$self->{current_file} = $current + 1;
next;
}
$line = <$fh>;
last;
}
return $line;
}
sub open_file {
my $self = shift;
my $name = $self->{names}->[ $self->{current_file} ];
open my $fh, '<', $name;
return $fh;
}
sub argv {
my $self = shift;
my $name = @{$self->{names}}
? $self->{names}->[ $self->{current_file} ]
: '-'
;
return $name;
}
sub input_line_number {
my $self = shift;
my $fh = @{$self->{names}}
? $self->{handles}->[$self->{current_file}]
: \*STDIN
;
return $fh->input_line_number;
}
который может быть использован в качестве:
package main;
use strict; use warnings;
my $it = My::ARGV::Reader->new(@ARGV);
echo($it);
sub echo {
my ($it) = @_;
printf "[%s:%d]:%s", $it, +$it, $_ while <$it>;
}
Выходной сигнал:
[file1:1]:bye bye [file1:2]:hello [file1:3]:thank you [file1:4]:no translation [file1:5]: [file2:1]:chao [file2:2]:hola [file2:3]:gracias [file2:4]:
Похоже, это уже реализовано как <код> Итератор :: Алмазное код> . Iterator :: Diamond также отключает магию открытия с двумя аргументами, которую использует perl при чтении < ARGV >
. Более того, он поддерживает чтение '-'
как STDIN, не задействуя всю другую магию. Фактически, я мог бы использовать это для этой цели только на отдельных файлах.