O acesso ao ângulo vazio do Perl “<>” operador de uma filehandle real?
-
08-07-2019 - |
Pergunta
Eu gosto de usar o recurso de perl bacana onde a leitura do <>
operador ângulo vazio magicamente dá seu programa semântica filtro UNIX, mas eu gostaria de ser capaz de aceder a esta funcionalidade através de um filehandle real (ou IO :: Handle objeto, ou similar), para que eu possa fazer coisas como passá-lo em sub-rotinas e tal. Existe alguma maneira de fazer isso?
Esta questão é particularmente difícil para o Google, porque a pesquisa de "operador de ângulo" e "filehandle" só me diz como ler filehandles usando o operador ângulo.
Solução
A partir perldoc perlvar
:
ARGV
O filehandle especial que itera sobre os nomes de arquivo de linha de comando em
@ARGV
. Normalmente escrito como a filehandle nulo na<>
operador ângulo. Note que atualmenteARGV
só tem seu efeito mágico dentro do operador<>
; em outros lugares, é apenas uma filehandle simples correspondente ao último arquivo aberto por<>
. Em particular, passando\*ARGV
como um parâmetro para uma função que espera um filehandle não pode causar a sua função para automaticamente ler o conteúdo de todos os arquivos em@ARGV
.
Eu acredito que as respostas todos os aspectos da questão em que "odeio dizer isso, mas não vai fazer o que quiser" espécie de caminho. O que você poderia fazer é funções fazem que levam uma lista de nomes de arquivos para abrir e fazer isso:
sub takes_filenames (@) {
local @ARGV = @_;
// do stuff with <>
}
Mas isso é provavelmente o melhor que você vai ser capaz de gerir.
Outras dicas
Expandindo a idéia de Chris Lutz, aqui é uma implementação muito rudimentar:
#!/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;
}
que pode ser usado como:
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>;
}
Output:
[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]:
Parece que isso já foi implementado como Iterator::Diamond
. Iterator :: Diamante também desabilita a magia 2-argumento-aberto que usa perl ao ler <ARGV>
. Melhor ainda, ele suporta a leitura '-'
como STDIN, sem habilitar todas as outras magia. Na verdade, eu poderia usá-lo para o efeito apenas em arquivos individuais.