如何将文件句柄作为参数传递给perl中的模块和subs

时间:2013-05-14 09:11:10

标签: perl pass-by-reference

我正在维护旧的Perl代码,需要在所有模块中启用严格的pragma。我在传递文件句柄作为模块和subs之间的引用时遇到问题。我们有一个公共模块负责打开日志文件,该文件作为typeglob引用传递。在其他模块中,run函数首先从公共模块调用open_log(),然后将此文件句柄传递给其他subs。

在这里,我写了一个简单的测试来模拟情况。

#!/usr/bin/perl -w
use strict;

$::STATUS_OK = 0;
$::STATUS_NOT_OK = 1;

sub print_header {
  our $file_handle = @_;

  print { $$file_handle } "#### HEADER ####"; # reference passing fails
}

sub print_text {
  my ($file_handle, $text)= @_;

  print_header(\$file_handle);
  print { $$file_handle } $text;
}

sub open_file_handle {
  my ($file_handle, $path, $name) = @_;

  my $filename = $path."\\".$name;
  unless ( open ($$file_handle, ">".$filename)) {
    print STDERR "Failed to open file_handle $filename for writing.\n";
    return $::STATUS_NOT_OK;
  }
  print STDERR "File $filename was opened for writing successfully.\n";
  return $::STATUS_OK;
}

my $gpath = "C:\\Temp";
my $gname = "mylogfile.log";
my $gfile_handle;

if (open_file_handle(\$gfile_handle, $gpath, $gname) == $::STATUS_OK) {
  my $text = "BIG SUCCESS!!!\n";
  print_text(\$gfile_handle, $text);
  print STDERR $text;
} else {
  print STDERR "EPIC FAIL!!!!!!!!\n";
}

Main函数首先调用open_file_handle并将文件句柄引用传递给print_text函数。如果我注释掉这一行:

print_header(\$file_handle);

一切正常,但我需要将文件句柄引用传递给print_text函数中的其他函数,这不起作用。

我是Java开发人员,Perl的参考处理对我来说并不熟悉。我不想更改open_log()子以返回文件句柄(现在它只返回状态),因为我有很多模块和数百个代码行可以在所有地方进行此更改。

如何修复代码以使其正常工作?

3 个答案:

答案 0 :(得分:8)

Perl中有两种类型的文件句柄。词法和全局裸字文件句柄:

open my $fh, '>', '/path/to/file' or die $!;
open FILEHANDLE, '>', '/path/to/file' or die $!;

你正在处理第一个问题,这很好。第二个是全球性的,不应该使用。

您拥有的文件句柄是词法,并且它们存储在标量变量中。它被称为标量,因为它有一个美元符号$。这些可以作为参数传递给subs。

foo($fh);

它们也可以被引用。在这种情况下,您会得到一个标量参考。

my $ref = \$fh;

通常你引用东西,如果你把它交给一个函数,所以Perl不会复制数据。可以想象像C中的指针一样的引用。它只是数据(结构)的内存位置。这段数据本身仍然存在。

现在,在您的代码中,您可以引用这些标量。您可以告诉,因为print语句中的$$fh语句已取消引用它。

sub print_text {
  my ($file_handle, $text)= @_;

  print_header(\$file_handle);
  print { $$file_handle } $text;
}

因此,作为参数得到的$file_handle= @_所做的)实际上是一个参考。将它传递给函数时,不需要再次引用它。

我猜你自己写了print_header

sub print_header {
  our $file_handle = @_;

  print { $$file_handle } "#### HEADER ####"; # reference passing fails
}

这里有一些事情: - our适用于全局变量。不要使用它。请改用my。 - 在参数赋值周围加上括号:my ($fh) = @_ - 由于您将对引用的引用传递给标量,因此需要取消引用两次:${ ${ $file_handle } }

当然,双击是很奇怪的。摆脱它将变量$file_hanlde传递给print_header而不是反对它:

sub print_text {
  my ($file_handle, $text)= @_;

  print_header($file_handle); # <-- NO BACKSLASH HERE
  print { $$file_handle } $text;
}

这就是你需要做的所有工作。

一般情况下,我会在这里删除对$file_handle变量的所有引用。你不需要它们。词法文件句柄已经是reference to an IO::Handle object,但现在不关心这一点,这并不重要。记住:

  • 使用前面有$的文件句柄
  • 在没有引用的情况下传递它们,您无需担心\${}以及类似的内容

有关详细信息,请参阅perlrefperlreftut

答案 1 :(得分:2)

您遇到了困难,因为您添加了多个额外级别的引用。像词法文件句柄这样的对象已经是引用。

如果您难以跟踪什么是引用,则可能需要使用某种匈牙利语表示法,例如_ref后缀。

print_text中,这将是:

sub print_text {
  my ($file_handle_ref, $text)= @_;

  print_header(\$file_handle_ref);
  print { $$file_handle_ref } $text;
}

print_header

sub print_header {
  my ($file_handle_ref_ref) = @_; # don't use `our`, and assign to a lvalue list!

  print { $$$file_handle_ref_ref } "#### HEADER ####"; # double derefernence … urgh
}

一个更好的解决方案是直接传递文件句柄,而不引用。

sub print_header {
  my ($file_handle) = @_;

  print {$file_handle} "#### HEADER ####"; # no reference, no cry
}

sub print_text {
  my ($file_handle, $text)= @_;

  print_header($file_handle);
  print {$file_handle} $text;
}

主要部分:

my $gpath = "C:/Temp"; # forward slashes work too, as long as you are consistent
my $gname = "mylogfile.log";

if (open_file_handle(\my $gfile_handle, $gpath, $gname) == $::STATUS_OK) {
  my $text = "BIG SUCCESS!!!\n";
  print_text($gfile_handle, $text);
  ...
} else {
  ...
}

答案 2 :(得分:0)

参考运算符是“\”(反斜杠)
任何包括数组,哈希甚至子例程的东西都可以被引用

向后计数的第5行

print_text(\$gfile_handle, $text);

您将引用的变量\$gfile_handle传递给子例程print_text

sub print_text {
    my ($file_handle, $text)= @_;

    print_header(\$file_handle);
    print { $$file_handle } $text;
}

在这个子例程中,$file_handle已经是参考文献 然后再次引用它并将其传递给子例程print_header

所以,你可以通过推迟参考操作员第5行向后计数来解决这个问题,如下所示:
    print_text($gfile_handle, $text);
再试一次: - )