2

古い Perl コードを維持しており、すべてのモジュールで厳密なプラグマを有効にする必要があります。モジュールとサブルーチン間の参照としてファイル ハンドルを渡す際に問題があります。型グロブ参照として渡されるログ ファイルを開くための共通モジュールがあります。他のモジュールでは、run 関数は最初に共通モジュールから open_log() を呼び出し、次にこのファイル ハンドルを他のサブルーチンに渡します。

ここでは、状況をシミュレートするための簡単なテストを作成しました。

#!/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()すべての場所でこの変更を行うには、多くのモジュールと何百ものコード行を通過する必要があるため、ファイルハンドルを返すようにサブルーチンを変更したくありません(現在はステータスのみを返します)。

コードを修正して機能させるにはどうすればよいですか?

4

3 に答える 3

8

Perl には 2 種類のファイルハンドルがあります。レキシカルおよびグローバル ベアワード ファイルハンドル:

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

あなたは最初のものを扱っています。これは良いことです。2 番目のものはグローバルであり、使用しないでください。

あなたが持っているファイルハンドルはレキシカルで、スカラー変数に格納されています。ドル記号があるため、スカラーと呼ばれ$ます。これらは引数としてサブルーチンに渡すことができます。

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) = @_ - スカラーへの参照への参照を渡すため、2 回逆参照する必要があります。${ ${ $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一般に、ここで varsへのすべての参照を削除します。それらは必要ありません。レキシカル ファイルハンドルはすでにIO::Handle オブジェクトへの参照ですが、今は気にしないでください。重要ではありません。覚えとけ:

  • $前もってあるファイルハンドルを使用する
  • 参照なしでそれらを渡すと、そのようなことを心配する必要はありませ\${}

詳細については、perlrefおよびperlreftutを参照してください。

于 2013-05-14T09:29:10.700 に答える