访问Perl的空角“<>”来自实际文件句柄的运算符?

时间:2009-10-29 00:29:16

标签: perl inputstream filehandle overloading

我喜欢使用漂亮的perl功能,其中从空角度运算符<>读取会神奇地为您的程序提供UNIX过滤器语义,但我希望能够通过实际的文件句柄(或{ {3}}对象,或类似的),这样我就可以做一些事情,比如将它传递给子程序等。有没有办法做到这一点?

这个问题特别难以谷歌,因为搜索“角度操作符”和“文件句柄”只是告诉我如何使用角度操作符从文件句柄中读取。

3 个答案:

答案 0 :(得分:9)

来自perldoc perlvar

  
      
  • ARGV
  •   
     

@ARGV中迭代命令行文件名的特殊文件句柄。通常在角度运算符<>中写为空文件句柄。请注意,目前ARGV仅在<>运算符中具有神奇效果;在其他地方,它只是一个普通的文件句柄,对应于<>打开的最后一个文件。特别是,将\*ARGV作为参数传递给需要文件句柄的函数可能不会导致您的函数自动读取@ARGV中所有文件的内容。

我相信这会回答你问题的所有方面,因为“讨厌说出来但不会做你想做的事”。你可以做的是创建一个打开文件名列表的函数,并执行以下操作:

sub takes_filenames (@) {
  local @ARGV = @_;
  // do stuff with <>
}

但这可能是你能够管理的最好的。

答案 1 :(得分:6)

扩展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, $_ 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]:

答案 2 :(得分:1)

看起来这已经实现为Iterator::Diamond。 Iterator :: Diamond还会禁用perl在阅读<ARGV>时使用的2参数开放魔法。更好的是,它支持将'-'作为STDIN读取,而不会启用所有其他魔法。事实上,我可能只是在单个文件上使用它。