如何使用模拟的“readline”函数自动分配到“$ _”?

时间:2011-02-22 17:44:18

标签: perl

Perl对readline函数(以及等效的<> I / O运算符)进行了一些特殊处理,它处理表达式

while (<HANDLE>)
while (readline(HANDLE))

等同于

while (defined($_ = <HANDLE>))

比照

$ perl -MO=Deparse -e 'f($_) while <>'
f($_) while defined($_ = <ARGV>);      <--- implicitly sets $_
-e syntax OK

但是,如果你劫持readline函数,那么这种自动分配似乎不会发生:

$ perl -MO=Deparse -e 'BEGIN {
> *CORE::GLOBAL::readline = sub { }
> }
> f($_) while <>'
sub BEGIN {
    *CORE::GLOBAL::readline = sub {
    };
}
f($_) while readline(ARGV);            <--- doesn't set $_ !
-e syntax OK

当然,这会使自定义readline函数对许多遗留代码的工作不正确。此代码的输出为"foo",其中包含BEGIN块,"bar"没有它,但我希望它为"BAR"

use warnings;
BEGIN { *CORE::GLOBAL::readline = \&uc_readline; }
sub uc_readline {
    my $line = CORE::readline(shift || *ARGV);
    return uc $line if defined $line;
    return;
}
($_, $bar) = ("foo\n", "bar\n");
open X, '<', \$bar;
while (<X>) {
  print $_;           # want and expect to see  "BAR\n"
}

我有哪些选项可以劫持readline功能,但仍能正确处理while (<...>)成语?在所有遗留代码中将所有内容显式转换为while (defined($_=<...>))是不切实际的。

2 个答案:

答案 0 :(得分:6)

这是一个相当肮脏的黑客使用重载来检测布尔上下文,但它似乎做了伎俩。在生产环境中使用此解决方案之前,它肯定需要比我给出的更多测试:

use warnings;
BEGIN { *CORE::GLOBAL::readline = \&uc_readline; }
sub uc_readline {
    my $line = CORE::readline(shift || *ARGV);
    return Readline->new(uc $line) if defined $line;
    return;
}

{package Readline;
    sub new {shift; bless [@_]}
    use overload fallback => 1,
        'bool' => sub {defined($_ = $_[0][0])},  # set $_ in bool context
        '""'   => sub {$_[0][0]},
        '+0'   => sub {$_[0][0]};
}

my $bar;
($_, $bar) = ("foo\n", "bar\n");
open X, '<', \$bar;
while (<X>) {
  print $_;           # want and expect to see  "BAR\n"
}

打印:

BAR

这也会使if (<X>) {...}设置为$_。我不知道是否有办法将魔法限制为仅while循环。

答案 1 :(得分:0)

此代码:

use warnings;
BEGIN { *CORE::GLOBAL::readline = \&uc_readline; }
sub uc_readline {
    my $line = CORE::readline(shift || *ARGV);
    return unless defined $line;
    $line = uc $line;
    $_ = $line;
    return $line;
}
($_, $bar) = ("foo\n", "bar\n");
open X, '<', \$bar;
while (<X>) {
  print $_;           # want and expect to see  "BAR\n"
}
print "$_";           # prints "BAR" instad of "foo"

几乎是正确的,但$ _不是本地化的,所以在循环之后,$ _被设置为从文件句柄读取的最后一个值。将Scope::Upper添加到组合中会修复:

use warnings;
use Scope::Upper qw/localize SCOPE/;
BEGIN { *CORE::GLOBAL::readline = \&uc_readline; }
sub uc_readline {
    my $line = CORE::readline(shift || *ARGV);
    return unless defined $line;
    $line = uc $line;
    local $_ = $line;
    # localize $_ in the scope of the while
    localize *main::_, \$line, SCOPE(1);
    return $line;
}
($_, $bar) = ("foo\n", "bar\n");
open X, '<', \$bar;
while (<X>) {
  print "$_";           # want and expect to see  "BAR\n"
}
print "$_";             # will print 'foo', not "BAR"