2013-05-14 107 views
2

我在维护旧的Perl代码,并且需要在所有模块中启用严格的编译指示。在传递文件句柄作为模块和子文件之间的引用时遇到问题。我们有一个公共模块,负责打开以typeglob引用传递的日志文件。在其他模块中,运行函数首先从公共模块调用open_log(),然后将该文件句柄传递给其他子类。如何在perl中将模块和子程序之间的文件句柄作为参考传递

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

#!/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"; 
} 

主函数首先调用open_file_handle并传递一个文件句柄参考print_text功能。如果我注释掉该行:

print_header(\$file_handle); 

一切工作正常,但我需要通过从print_text功能的文件句柄参考其他功能,这是行不通的。

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

如何修复我的代码以使其工作?

回答

7

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 } }

当然双DEREF是怪异。摆脱它传递变量$file_hanldeprint_header而不是refence它的:

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,但现在不用担心,它并不重要。请记住:有一个$前面

  • 通过他们没有引用

    • 使用的文件句柄,你不必担心\${}之类的东西

    更多info,参见perlrefperlreftut

  • +0

    非常感谢。 “我们”和double-deref只是我的试验,试图找出我如何使它工作。实际的错误似乎是在参数分配周围没有放置括号。我只是想了解如何在perl中引用工程。这有助于我理解其中的一些。 – edufinn 2013-05-14 09:51:30

    +0

    @edufinn我很高兴它的工作原理。 :)你应该阅读我链接的教程。引用是非常强大的,也是Perl的一个组成部分。但是,标量的有点奇怪。尝试使用数据结构。使用Data :: Dumper来查看一些东西,你会更快地理解它。 – simbabque 2013-05-14 10:52:07

    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 { 
        ... 
    } 
    
    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);
    ,然后再试一次:-)

    相关问题