2009-03-05 25 views
11

当我通过的Perl编程,第2版,第51页,阅读的东西混淆了我:为什么编程Perl使用本地(而不是我的)文件句柄?

sub newopen { 
    my $path = shift; 
    local *FH; #not my! 
    open (FH, $path) || return undef; 
    return *FH; 
} 

$fh = newopen('/etc/passwd'); 

我,我知道,我们为什么不能重新开始使用我的?到目前为止,如果我们使用my(),我不会看到任何错误。

谢谢!

+7

编程Perl,第三版是很久以前写在远处的一个银河系里的。而且你有一个比这更老的版本! – 2009-09-18 22:43:50

+0

...现在我们到了第6版,我想 – osirisgothra 2014-09-01 16:16:38

回答

25

陈述的答案是,你必须使用local,因为my *FH是一个语法错误。

“正确”(但不是非常有启发性)的答案是,你做错了。您应该使用词法文件句柄和open的三参数形式。

sub newopen { 
    my $path = shift; 
    my $fh; 
    open($fh, '<', $path) or do { 
     warn "Can't read file '$path' [$!]\n"; 
     return; 
    } 
    return $fh; 
} 

要真正回答为什么需要词汇和全局变量之间的和可变的范围和持续时间之间的差异的解释。

变量的作用域是程序中名称有效的部分。范围是一个静态属性。另一方面,变量的持续时间是一个动态属性。持续时间是指程序执行期间变量存在并保存一个值的时间。

my声明了一个词法变量。词法变量具有从声明点到封闭块(或文件)结束的范围。您可以在不同范围内使用相同名称的其他变量而不会发生冲突。 (您也可以在重叠范围内重复使用名称,但不这样做。)词汇变量的持续时间通过引用计数进行管理。只要至少有一个对变量的引用存在,即使该名称在特定范围内无效! my也具有运行时效果 - 它分配新的变量与给定的名称。

local有点不同。它对全局变量进行操作。全局变量具有全局范围(该名称无处不在)和程序整个生命周期的持续时间。 local所做的就是临时更改全局变量的。这有时被称为“动态范围”。更改从local声明开始,并一直保留到封闭块的结尾,之后旧值被恢复。值得注意的是,新值并不局限于块 - 它随处可见(包括被称为子例程)。引用计数规则仍然适用,因此您可以在更改结束后采取并保留对本地化值的引用。

返回本示例:*FH是一个全局变量。更准确地说,它是一个“typeglob” - 一组全局变量的容器。一个typeglob包含一个用于每个基本变量类型(标量,数组,散列)以及其他一些事物的槽。历史上,Perl使用typeglobs来存储文件句柄和local - 它们帮助确保它们不会互相打破。词汇变量没有typeglobs,这就是为什么说my *FH是语法错误。

在现代版本的Perl词法变量中,可以并应该用作文件句柄。这让我们回到了“正确”的答案。

12

你为什么要读一本过时的书。第三版已经出来很久了!你使用的是哪个版本的Perl?第二版描述Perl 5.004(5.4.x)或其附件。

现在,您不应该对文件句柄使用typeglob表示法;请使用'词法文件句柄'(请参阅open,我认为)或FileHandle模块或其亲属之一。


感谢Michael Schwern和Ysth对此处的评论。

+0

在现实世界中,您确实需要处理大量遗留代码。 – 2009-03-05 08:08:50

+0

第二版是5.004ish,我认为(现代说法是5.4.x)。 – ysth 2009-03-05 17:51:17

+0

@Yan在现实世界中,很少有人使用5.6以上的东西。 @Jonathan FileHandle仅仅是打开和阅读文件而已。词汇文件句柄++ – Schwern 2009-03-05 21:27:11

0

我认为这是因为my在堆栈中分配了一个新的变量副本,并且在退出块时它会丢失。 local将其他地方的现有*FH保存并覆盖现有的*FH。当你退出堆栈时它会恢复旧的。 my当您退出该块时,*FH typeglob超出范围。随着local它继续存在,所以你可以继续使用它后返回它。

我不是100%确定这一点,但也许它可以指出你在正确的方向。

0

请参阅本地化文件句柄here,我想这可以解释它。

20

在您的示例代码中,对内置子程序open的调用使用的是裸文字作为文件句柄,这与全局变量相当。如Nathan Fellman's answer所解释的那样,如果在脚本或模块的其他地方定义了另一个具有相同名称的全局变量,则使用local将本裸号字本地化到当前代码块。这将阻止先前定义的全局变量被新声明消除。

这是在旧的Perl天非常普遍的做法,但和Perl 5.6的它远不如用一个标量(与你在你的问题暗示的my声明)来定义你的文件句柄和另外,使用三个参数调用open

use Carp; 
open my $error_log, '>>', 'error.log' or croak "Can't open error.log: $OS_ERROR"; 

顺便说一句,请注意,标准输入/输出阅读和写作,它仍然是更好地使用这两个参数open

use Carp; 
open my $stdin, '<-' or croak "Can't open stdin: $OS_ERROR"; 

或者,您可以使用IO::File模块祝福档案文件句柄:

use IO::File; 
my $error_log = IO::File->new('error.log', '>>') or croak "Can't open error.log: $OS_ERROR"); 

这里的大部分功劳归于Damian Conway, author of the excellent book Perl Best Practices。如果你对Perl开发很认真,你应该自己购买这本书。

相关问题