从实际文件句柄访问 Perl 的空角“<>”运算符?
-
08-07-2019 - |
题
我喜欢使用漂亮的 Perl 功能,从空角运算符读取 <>
神奇地为您的程序提供 UNIX 过滤器语义,但我希望能够通过实际的文件句柄(或 IO::句柄 对象,或类似的),这样我就可以做一些事情,比如将它传递到子例程等。有什么办法可以做到这一点吗?
这个问题特别难用谷歌搜索,因为搜索“角度运算符”和“文件句柄”只是告诉我如何使用角度运算符从文件句柄中读取数据。
解决方案
ARGV
迭代命令行文件名的特殊文件句柄
@ARGV
. 。通常在角度运算符中写为空文件句柄<>
. 。请注意,目前ARGV
只有在范围之内才有它的神奇功效<>
操作员;在其他地方,它只是一个简单的文件句柄,对应于最后打开的文件<>
. 。特别是,通过\*ARGV
作为需要文件句柄的函数的参数可能不会导致您的函数自动读取中所有文件的内容@ARGV
.
我相信这以“不想这么说,但它不会做你想做的事”的方式回答了你问题的各个方面。您可以做的是创建需要打开文件名列表的函数,然后执行以下操作:
sub takes_filenames (@) {
local @ARGV = @_;
// do stuff with <>
}
但这可能是您能够做到的最好的了。
其他提示
扩展Chris Lutz的想法,这是一个非常基本的实现:
#!/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, 扩展Chris Lutz的想法,这是一个非常基本的实现:
#!/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;
}
可以用作:
[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]:
输出:
<*> while <$it>;
}
输出:
<*>看起来这已经实现为 <代码>迭代::钻石代码> 。 Iterator :: Diamond还会禁用perl在读取&lt; ARGV&gt;
时使用的2参数打开魔法。更好的是,它支持将' - '
作为STDIN读取,而不会启用所有其他魔法。事实上,我可能只是在单个文件上使用它。
不隶属于 StackOverflow