Perl typeglobs和Real References:如果我写入\ * STDOUT而不是* STDOUT,我会得到什么?

时间:2012-05-07 06:43:38

标签: perl

如果我写一个真实的引用,例如\*STDOUT而不是类似*STDOUT的类型,我会获得什么?

4 个答案:

答案 0 :(得分:8)

一个是typeglob,另一个是对它的引用。

据我所知,主要的实际区别在于你不能将typeglob加入对象,但你可以祝福typeglob引用(这是IO::Handle所做的)

这一区别在“Perl Cookbook”,Recipe 7.16中有详细讨论。 “将文件句柄存储在变量中”。


另一个不同之处在于,分配一个glob会为ENTIRE glob创建一个别名,而分配一个glob引用会产生预期的效果(如perldoc perlmod, "Symbol Tables" section中所述。为了说明:

@STDOUT=(5);
$globcopy1 = *STDOUT; # globcopy1 is aliased to the entire STDOUT glob, 
                      # including alias to array @STDOUT
$globcopy2 = \*STDOUT; # globcopy2 merely stores a reference to a glob, 
                       # and doesn't have anything to do with @STDOUT

print $globcopy1 "TEST print to globcopy1/STDOUT as filehandle\n";
print "TEST print of globcopy1/STDOUT as array: $globcopy1->[0]\n\n";
print $globcopy2 "TEST print to globcopy2/STDOUT as filehandle\n";
print "TEST print of globcopy2/STDOUT as array: $globcopy2->[0]\n\n";

产地:

TEST print to globcopy1/STDOUT as filehandle
TEST print of globcopy1/STDOUT as array: 5

TEST print to globcopy2/STDOUT as filehandle
Not an ARRAY reference at line 8.

作为旁注,传闻说typeglob引用是将文件句柄传递给函数的唯一方法不是这样:

sub pfh { my $fh = $_[0]; print $fh $_[1]; }

pfh(*STDOUT, "t1\n");
pfh(\*STDOUT, "t2\n");

# Output:
# t1
# t2

答案 1 :(得分:4)

*表示一个typeglob,它是符号表中的一个条目。例如:

my $x;
print *x;

将产生*main::x

\表示参考。试试这个:

use YAML::XS;

print Dump *STDOUT;
print Dump \*STDOUT;

第一个是glob,第二个是参考。大概是你这样做的时候:

my $fh = *STDOUT;

您实际上是将glob复制到一个新条目中,虽然我认为这不是很有意义 - 如果您现在close STDOUT$fh也会关闭。这样:

my $fh = \*STDOUT;

只是一个参考,这是首选。另见:

http://perldoc.perl.org/perldata.html#Typeglobs-and-Filehandles

答案 2 :(得分:3)

您将获得对名为STDOUT的typeglob的引用。请参阅perlref

以下是对此的说法:

  

无法创建对IO句柄的真实引用   (filehandle或dirhandle)使用反斜杠运算符。你最   can get是对typeglob的引用,实际上是一个完整的   符号表条目。但请参阅* foo {THING}语法的解释   下面。但是,您仍然可以使用类型globs和globrefs   他们是IO句柄。

perldata也很有用。

总之,它就像构建对文件句柄的引用。您可以将其作为一个使用。但是使用typeglob,你也可以做其他事情。符号表包含名为STDOUT的所有变量的值,即$STDOUT@STDOUT%STDOUT甚至&STDOUT,以及文件句柄。只需一个typeglob就可以访问它们。但是如果是STDOUT,您可能不必担心,因为您的代码中可能不会有%STDOUT

这种引用也是将文件句柄作为参数传递给函数的唯一方法。

sub myprint {
  my $fh = shift;
  print $fh "Hello World!\n";
}
&myprint(\*STDOUT);

答案 3 :(得分:1)

Perl提供了3种获取相同文件句柄的方法:

  • *STDOUTtypeglob
  • \*STDOUT是对typeglob的引用
  • *STDOUT{IO}是undef或对IO :: Handle的引用

我现在更喜欢使用\*STDOUT来保持一致性。当我使用open(my $handle, ...)自动生成文件句柄时,Perl给我一个typeglob的引用。因此,如果我想使用STDOUT或打开文件,我可能会写

my $handle;
if ($path eq "-") {
    $handle = \*STDOUT;
} else {
    open($handle, '>', $path) or die "Can't open $path: $!";
}

所以我的句柄始终是对typeglob的引用。这种一致性并不太重要。我的其余代码不应该关心它是否引用了typeglob或其他类型的文件句柄。

鉴于我对\*FH的偏好,我很惊讶地发现*FH有效但\*FH失败的示例。在perlsub, "Pass by Reference"中,我找到了这个例子:

  

如果您计划生成新的文件句柄,则可以执行此操作。注意只传回裸* FH,而不是它的参考。

sub openit {
    my $path = shift;
    local *FH;
    return open (FH, $path) ? *FH : undef;
}

它确实需要传回*FH,而不是\*FH,尽管perlsub没有解释原因。我通过在脚本中调用sub来验证它自己:

use strict;
use warnings;

sub openit {
    my $path = shift;
    local *FH;
    return open (FH, $path) ? *FH : undef;
}

my $it = openit '</etc/fstab' or die "Can't open it: $!";
if (defined($_ = <$it>)) {
    print "Its first line is $_";
} else {
    print "It has no first line\n";
}

当返回值为*FH时,脚本可以正常工作。当我将其更改为\*FH时,脚本将失败。因为我正在使用警告,我看到了:

readline() on unopened filehandle FH at scratch.pl line 11.
It has no first line

这是因为\*FH指向全局变量。在sub中,local *FH为全局typeglob提供了临时值。返回时,*FH丢失了临时值,FH又恢复为未打开的文件句柄。

修复方法是复制临时值。当子返回*FH时会发生这种情况。它复制了typeglob,因为它不是左值子。现在我知道为什么sub必须返回*FH,而不是\*FH

如果我想让sub返回引用,我可以复制typeglob然后参考:

local *FH;
return open (FH, $path) ? \(my $glob = *FH) : undef;

或者我可以自动生成参考:

my $globref;
return open ($globref, $path) ? $globref : undef;

如果更改STDOUT的值并且需要复制旧值,则必须使用*STDOUT复制typeglob,而不是\*STDOUT。您可能不需要复制该值。