2013-05-14 24 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);
    ,然後再試一次:-)

    相關問題