【问题标题】:Access to Perl's empty angle "<>" operator from an actual filehandle?从实际文件句柄访问 Perl 的空角“<>”运算符?
【发布时间】:2009-10-29 00:29:16
【问题描述】:

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

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

【问题讨论】:

  • 不清楚你在寻找什么行为。据我所知, 和 之间的唯一区别是 如果给出了文件名参数(@ARGV),它们将退回到文件名参数(@ARGV)。是这个意思吗?
  • 我认为他想将&lt;&gt; 文件句柄传递给函数。
  • 啊啊啊,我现在明白了。我倒读了这个问题......
  • 是的,克里斯,这正是我想要做的。但我最终使用了local @ARGV,然后只是阅读&lt;ARGV&gt;。谢谢大家。

标签: perl inputstream filehandle overloading


【解决方案1】:

来自perldoc perlvar

  • ARGV

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

我相信这会以“讨厌说出来,但它不会做你想做的事”的方式回答你问题的所有方面。你可以做的是创建一个打开文件名列表的函数,然后这样做:

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

但这可能是你能做到的最好的。

【讨论】:

  • 当然,我忘记了另一个答案,那就是创建一个类,该类接受许多文件名并重载&lt;&gt; 运算符来读取它们,就像&lt;&gt;@ARGV 所做的那样.它甚至具有一些超强的可扩展性潜力。
  • &lt;&gt; 的好处是如果没有文件名,它会从STDIN 读取。
  • 是的,我们可能会失去它(至少添加会很尴尬),但我们会获得传递它的能力。
  • 其实我后来发现&lt;&gt;只是&lt;ARGV&gt;的快捷方式。我一直认为这是魔术。现在我知道 真正的 魔法在ARGV 文件句柄中。但无论如何,我最终意识到有更好的方法来构建我的程序。不过,我确实使用了local @ARGV 技巧。
【解决方案2】:

扩展 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>;
}

输出:

[文件1:1]:再见 [文件1:2]:你好 [文件1:3]:谢谢 [file1:4]:没有翻译 [文件1:5]: [文件2:1]:超 [文件2:2]:你好 [文件2:3]:格拉西亚斯 [文件2:4]:

【讨论】:

  • Awww... [在自己的实施开始时看起来很狡猾...]
  • 我真的很喜欢你的字符串解释过载来打印当前文件名。这真是太聪明了。
  • @Chris Lutz:谢谢。我终于设法弄清楚如何通过重载访问当前行号。有趣的东西。
【解决方案3】:

看起来这已经实现为Iterator::Diamond。 Iterator::Diamond 还禁用了 perl 在读取 &lt;ARGV&gt; 时使用的 2-argument-open 魔法。更好的是,它支持将 '-' 读取为 STDIN,而无需启用所有其他魔法。事实上,我可能只在单个文件上使用它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-13
    相关资源
    最近更新 更多