Perl中的错误或者我对regexp匹配和perl变量一无所知?

时间:2011-06-17 12:59:13

标签: perl

很长一段时间以来,我总是认为Perl subs中的参数是按值传递的。现在,我遇到了一些我不理解的东西:

use strict;
use warnings;

use Data::Dumper;

sub p {
    print STDERR "Before match: " . Data::Dumper->Dump([[@_]]) . "\n";
    "1" =~ /1/;
    print STDERR "After  match: " . Data::Dumper->Dump([[@_]]) . "\n";
}

my $line = "jojo.tsv.bz2";

if ($line =~ /\.([a-z0-9]+)(?:\.(bz2|gz|7z|zip))?$/i) {
    p($1, $2 || 'none');
    p([$1, $2 || 'none']);
}

首次调用p()时,执行regexp匹配后,@ _中的值将变为undefs。在第二次调用时,一切正常(因为数组引用传递的值不受影响)。

使用Perl版本5.8.8(CentOS 5.6)和5.12.3(Fedora 14)进行测试。

问题是 - 如何发生这种情况,regexp匹配会破坏使用$ 1,$ 2等构建的@_的内容(如果添加它们,其他值不受影响)?

3 个答案:

答案 0 :(得分:15)

perlsub man page说:

  

数组@_是一个本地数组,但它的元素是实际标量参数的别名。

因此,当您将$1传递给子例程时,该子例程$_[0]内部是$1别名,而不是副本 $1。因此,它会通过p中的正则表达式匹配进行修改。

通常,每个Perl子例程的开头应该如下所示:

my @args = @_;

......或者这个:

my ($arg1, $arg2) = @_;

......或者这个:

my $arg = shift;

任何捕获正则表达式都应该像这样使用:

my ($match1, $match2) = $str =~ /my(funky)(regexp)/;

如果没有这些规则,你可能会被微妙的错误驱使。

答案 1 :(得分:4)

正如所建议的那样,复制每个sub中的args是一个好主意(如果只是通过给它们一个非标点符号来记录它们的内容)。

但是,永远不要传递全局变量也是个好主意;传递"$1", "$2",而不是$1, $2。 (这也适用于$DBI::errstr之类的内容。)

答案 2 :(得分:0)

我不太清楚为什么会这样,但我会说你应该使用类似的东西     我的$ arg1 =班次;     我的$ arg2 =班次; 并在你的sub中使用$ arg1和$ arg2。

使用perl调试器,您将看到@_在2个子调用中看起来不同:

第一个电话:比赛前:

x @_
0  'tsv'
1  'bz2'

比赛结束后:

x @_
0  undef
1  undef

我认为这被比赛覆盖了。

第二个电话:比赛前:

x @_
0  ARRAY(0xc2b6e0)
    0  'tsv'
    1  'bz2'

比赛结束后:

x @_
0  ARRAY(0xc2b6e0)
    0  'tsv'
    1  'bz2'

所以也许这不会被覆盖,因为结构不同(?)。

希望这有点帮助。