Accesso all'angolo vuoto di Perl & # 8220; < > & # 8221; operatore da un filehandle effettivo?
-
08-07-2019 - |
Domanda
Mi piace usare la funzionalità perl elegante in cui la lettura dall'operatore dell'angolo vuoto < >
fornisce magicamente la semantica del filtro UNIX al programma, ma mi piacerebbe poter accedere a questa funzione tramite un filehandle effettivo (o IO :: Handle o simile) , in modo che io possa fare cose come passarlo in subroutine e simili. C'è un modo per farlo?
Questa domanda è particolarmente difficile da google, perché la ricerca di "operatore angolare" e " filehandle " mi dice solo come leggere dai filehandle usando l'operatore angle.
Soluzione
Da perldoc perlvar
:
ARGV
Il filehandle speciale che scorre sui nomi dei file della riga di comando in
@ARGV
. Solitamente scritto come filehandle null nell'operatore dell'angolo< >
. Nota che attualmenteARGV
ha solo il suo effetto magico all'interno dell'operatore< >
; altrove è solo un semplice filehandle corrispondente all'ultimo file aperto da< >
. In particolare, passare\ * ARGV
come parametro a una funzione che prevede un filehandle potrebbe non causare la lettura automatica del contenuto di tutti i file in@ARGV
.
Credo che risponda a tutti gli aspetti della tua domanda in quello "Odio dirlo, ma non farà ciò che vuoi" tipo di modo. Quello che potresti fare è creare funzioni che aprano un elenco di nomi di file, e fai questo:
sub takes_filenames (@) {
local @ARGV = @_;
// do stuff with <>
}
Ma questo è probabilmente il migliore che sarai in grado di gestire.
Altri suggerimenti
Espandendo l'idea di Chris Lutz, ecco un'implementazione molto rudimentale:
#!/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;
}
che può essere utilizzato come:
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, Espandendo l'idea di Chris Lutz, ecco un'implementazione molto rudimentale:
#!/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;
}
che può essere utilizzato come:
[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]:
Output:
<*> while <$it>;
}
Output:
<*> Sembra che questo sia già stato implementato come Iterator :: Diamond
. Iterator :: Diamond disabilita anche la magia aperta a 2 argomenti che perl usa quando legge < ARGV >
. Ancora meglio, supporta la lettura di '-'
come STDIN, senza abilitare tutta l'altra magia. In effetti, potrei usarlo a tale scopo solo su singoli file.