当我阅读 Programming Perl ,第2版,第51页时,有些东西让我困惑:
sub newopen {
my $path = shift;
local *FH; #not my!
open (FH, $path) || return undef;
return *FH;
}
$fh = newopen('/etc/passwd');
我知道,为什么我们不会重新开始使用我的?到目前为止,如果我们使用my(),我看不出任何问题。
谢谢!
答案 0 :(得分: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;
}
要真正回答为什么需要解释词法变量和全局变量之间以及变量范围和持续时间之间的区别。
变量的范围是程序中名称有效的部分。 Scope是一个静态属性。另一方面,变量的持续时间是动态属性。持续时间是程序执行期间变量存在并保存值的时间。
my
声明一个词法变量。词法变量的范围从声明点到封闭块(或文件)的末尾。您可以在不同范围内使用相同名称的其他变量而不会发生冲突。 (您也可以在重叠范围中重复使用名称,但不要这样做。)词汇变量的持续时间通过引用计数进行管理。只要存在至少一个对变量的引用,即使该名称在特定范围内无效! my
也有运行时效果 - 它会使用给定名称分配 new 变量。
local
有点不同。它适用于全局变量。全局变量具有全局范围(名称在任何地方都有效)以及程序整个生命周期的持续时间。 local
的作用是对全局变量的值进行临时更改。这有时被称为“动态范围”。更改从local
声明开始,一直持续到封闭块结束,之后将恢复旧值。重要的是要注意新值不限于块 - 它在任何地方都可见(包括被调用的子例程)。引用计数规则仍然适用,因此您可以在更改过期后获取并保留对本地化值的引用。
回到示例:*FH
是一个全局变量。更准确地说,它是一个“typeglob” - 一组全局变量的容器。 typeglob包含每个基本变量类型(标量,数组,哈希)的插槽以及一些其他内容。从历史上看,Perl使用typeglobs来存储文件句柄和local
- 它们有助于确保它们不会相互破坏。词法变量没有typeglobs,这就是说my *FH
是语法错误的原因。
在Perl的现代版本中,词汇变量可以而且应该用作文件句柄。这让我们回到了“正确”的答案。
答案 1 :(得分: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开发,那么您自己就可以购买本书了。
答案 2 :(得分:12)
你为什么要读一本过时的书。第3版已经出了很长时间了!您使用的是哪个版本的Perl?第2版描述了Perl 5.004(5.4.x)或其附近。
现在,您不应该对文件句柄使用typeglob表示法;使用'词汇文件句柄'(参见open,我认为)或FileHandle模块或其中一个亲戚。
感谢Michael Schwern和Ysth在此收到的意见。
答案 3 :(得分:0)
我相信这是因为my
在堆栈上分配了一个新变量,当你退出块时它就丢失了。 local
会将现有*FH
保存在其他位置,并覆盖现有*FH
。退出堆栈时,它会恢复旧的。使用my
时,*FH
typeglob会在退出块时超出范围。 local
保持现有状态,因此您可以在退回后继续使用它。
我不是百分之百确定这一点,但也许它可以指出你正确的方向。
答案 4 :(得分:0)
请参阅本地化文件句柄here,我想这可以解释它。