Acceso al ángulo vacío de Perl & # 8220; < > & # 8221; operador de un filehandle real?
-
08-07-2019 - |
Pregunta
Me gusta usar la ingeniosa función perl donde leer desde el operador de ángulo vacío < >
mágicamente le da a su programa semántica de filtro UNIX, pero me gustaría poder acceder a esta función a través de un identificador de archivo real (o IO :: Handle , o similar) , para poder hacer cosas como pasarlo a subrutinas y demás. ¿Hay alguna forma de hacer esto?
Esta pregunta es particularmente difícil de google, porque la búsqueda de " operador de ángulo " y "filehandle" solo me dice cómo leer de los controladores de archivos usando el operador de ángulo.
Solución
De perldoc perlvar
:
ARGV
El identificador de archivo especial que itera sobre los nombres de archivo de la línea de comandos en
@ARGV
. Por lo general, se escribe como el identificador de archivo nulo en el operador de ángulo< >
. Tenga en cuenta que actualmenteARGV
solo tiene su efecto mágico dentro del operador< >
; en otros lugares es solo un identificador de archivo simple correspondiente al último archivo abierto por< >
. En particular, pasar\ * ARGV
como parámetro a una función que espera un identificador de archivo puede no hacer que su función lea automáticamente el contenido de todos los archivos en@ARGV
.
Creo que eso responde a todos los aspectos de su pregunta en ese " Odio decirlo, pero no hará lo que quiere " tipo de camino Lo que podría hacer es crear funciones que tomen una lista de nombres de archivo para abrir, y haga esto:
sub takes_filenames (@) {
local @ARGV = @_;
// do stuff with <>
}
Pero eso es probablemente lo mejor que podrá administrar.
Otros consejos
Ampliando la idea de Chris Lutz, aquí hay una implementación muy rudimentaria:
#!/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 se puede usar 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, Ampliando la idea de Chris Lutz, aquí hay una implementación muy rudimentaria:
#!/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 se puede usar como:
[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]:
Salida:
<*> while <$it>;
}
Salida:
<*> Parece que esto ya se ha implementado como Iterator :: Diamond
. Iterator :: Diamond también deshabilita la magia abierta de 2 argumentos que Perl usa cuando lee < ARGV >
. Aún mejor, admite leer '-'
como STDIN, sin habilitar toda la otra magia. De hecho, podría usarlo para ese propósito solo en archivos individuales.