当我读完 Perl编程, ,第二版,第 51 页,有些事情让我感到困惑:

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

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

我的我知道,为什么我们不重新开始使用我的?到目前为止,我看不出如果我们使用 my() 会出现什么问题。

谢谢!

有帮助吗?

解决方案

答案是,您必须使用 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的现代版本中,词汇变量可以而且应该用作文件句柄。这让我们回到了“正确”的境地。答案。

其他提示

在您的示例代码中,对内置子例程 open 的调用使用一个简单的单词作为文件句柄,它相当于一个全局变量。正如 Nathan Fellman的回答所解释的那样,使用如果在脚本或模块中的其他位置定义了具有相同名称的另一个全局变量,则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,优秀书Perl Best Practices的作者。如果您认真对待Perl开发,那么您自己就可以购买本书了。

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

如今,您不应该对文件句柄使用 typeglob 表示法;使用“词法文件句柄”(参见 打开, ,我认为)或 文件句柄 模块,或其亲属之一。


感谢 Michael Schwern 和 Ysth 在此提出评论。

我相信这是因为 my 在堆栈上分配了一个新的变量副本,当你退出块时它就丢失了。 local 将现有的 * FH 保存在其他位置,并覆盖现有的 * FH 。退出堆栈时,它会恢复旧的。使用 my 时, * FH typeglob在退出块时超出范围。使用 local ,它会保持现有状态,因此您可以在返回后继续使用它。

我不是百分之百确定这一点,但也许它可以指出你正确的方向。

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top