如何在Perl中将文件名传递给子例程?

4
我是一个Perl脚本编写者,我想向子例程传递一个输出文件的文件名。我尝试过以下方法:
use strict;
use warnings;

test("Output.dat");

sub test {
  my $name = @_;

  open(B, ">$name") or die "Failure \n";

  print B "This is a test! \n";
  close(B); 
}

我将多次使用子程序,因此必须传递文件名,不能在子程序内部声明。

希望能对您有所帮助 :)


2
你必须在列表语境中(注意括号)分配输入变量 my ($name) = @_;,另请参阅 http://perlmaven.com/scalar-and-list-context-in-perl - mpapec
3个回答

7
你的问题在于这一行代码:
my $name = @_;

你正在将一个数组分配给一个标量变量。在Perl中,这将为您提供数组中的元素数量 - 因此,我希望您最终在$name中得到“1”。
有许多方法可以从数组中获取第一个元素;
my $name = $_[0]; # Explicitly get the first element
my $name = shift @_; # Get the first element (and remove it from the array)
my $name = shift; # Same as the previous example - shift works on @_ by default in a subroutine
my ($name) = @_; # The parentheses make it into a list assignment

最常见的是最后两个。
还有一些要点:
1/ 如果您在错误消息中包含了 $name,您将更好地了解问题的线索。
open(A, ">$name") or die "Failure: $name \n";

或者,更好的方式是让Perl从你的操作系统获取错误信息。
open(A, ">$name") or die "Could not open $name: $!\n";

我已经添加了缺失的逗号 - 我认为那是一个笔误。

2/ 现在,使用三参数的open和词法文件句柄通常被视为良好的实践。

open(my $output_fh, '>', $name) or die "Failure: $name \n";

3/ 在您的例子中,您打开了一个名为"A"的文件句柄,但是尝试写入一个名为"B"的文件句柄。这是否是一个笔误?


谢谢!这很有帮助!是的,你说得对。我在文件句柄"A"和"B"上打了一些错字 :) - nieka
关于1/:如果您同时包含$!,那么这将是一个更好的提示。 - Wolf
@Wolf:我想我正在编辑我的答案,几乎与你建议的同时添加那个 :-) - Dave Cross

4
my $name = @_;

在标量模式下,将把@_数组的元素数量赋给$name。这意味着它是参数的数量。这很可能不是您想要的。因此,您必须将数组分配给数组或标量分配给标量。你有两个选项。

my $name = $_[0];

或者

my ($name) = @_; # or even (my $name) = @_;

我更喜欢稍后使用的方法,因为它可以轻松修改为my ($a, $b, $c) = @_;,这是Perl语言的惯用法。

但是你的代码还有更多缺陷。例如,你应该使用这种open形式。

open my $fd, '>', $name or die "cannot open > $name: $!";

这种方式有一些优势。首先,您使用了词法作用域的IO句柄,防止其泄漏到词法作用域之外,并在退出该词法作用域时自动关闭。第二,列表形式防止解释 $name 内容以外的文件名。

因此,生成的代码应如下所示:

sub test {
    my ($name) = @_;

    open my $fd, '>', $name
        or die "cannot open > $name: $!";

    print $fd "This is a test!\n";
}

很棒,这里展示了open的三参数版本以及$!中的错误代码。顺便问一下:不使用括号的好处是什么? - Wolf
嗯 :-) ...将OP的close包含在结果代码片段中,这不是很好吗?或者使用my变量有自动化机制吗? - Wolf
哦,抱歉,我没有仔细阅读,你已经写了:词法作用域IO句柄,防止泄漏到词法作用域之外,并在退出此词法作用域时自动关闭 - 现在我明白了,谢谢! - Wolf

0
在回答您的问题之前,我想建议一件事 -
始终使用具有三个参数的open()版本,例如 -
open (my $FH, '>', 'file.txt') or die "Cannot open the file:$!";

如果您将单个参数传递给子程序,可以使用“shift”运算符。
test("Output.dat");

sub test {
    my $name = shift;

    open (my $B, '>', $name) or die "Cannot open the file:$!";
    print $B "This is a test! \n";

    close($B); 
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接