为什么版本 - >解析无法在没有先前打印的情况下工作?

时间:2011-07-19 22:59:12

标签: perl version

我不得不承认,这个让我感到厌恶。

考虑以下代码:

use version;
use Data::Dumper;
my $codeLevel = q{6.1.0.7 (build 25.3.1103030000)};
print STDERR qq{$codeLevel\n};
my $vrmf;
if($codeLevel =~ /^\s*([0-9.]*) \(build.*\)/) { 
    print STDERR "$1\n";
    $vrmf = version->parse($1);
}

print STDERR Dumper($vrmf);

正如预期的那样,输出是:

6.1.0.7 (build 25.3.1103030000)
6.1.0.7
$VAR1 = bless( {
                 'original' => '6.1.0.7',
                 'qv' => 1,
                 'version' => [
                                6,
                                1,
                                0,
                                7
                              ]
               }, 'version' );

但是,删除第二个印刷品:

use version;
use Data::Dumper;
my $codeLevel = q{6.1.0.7 (build 25.3.1103030000)};
print STDERR qq{$codeLevel\n};
my $vrmf;
if($codeLevel =~ /^\s*([0-9.]*) \(build.*\)/) {
    $vrmf = version->parse($1);
}
print STDERR Dumper($vrmf);

输出变为:

6.1.0.7 (build 25.3.1103030000)
$VAR1 = bless( {
                 'original' => '0',
                 'version' => [
                                0
                              ]
               }, 'version' );

我找不到任何文档说明print会影响传递给它的变量,或者它会影响正则表达式匹配变量。

有人可以向我解释这里发生了什么事吗?

5 个答案:

答案 0 :(得分:5)

Perl中的标量值可以是数字,同时也可以是字符串 。 SV对象(SV =标量值)具有整数,浮点和字符串值的槽,以及标识哪些值在任何时间点有效的标记。当您使用值作为字符串时,perl会计算字符串值并设置一个标识为有效的标志。 (其他操作,如添加1会使字符串值无效。)当您打印某些内容时(不出所料)将其用作字符串。你可以使用Devel :: Peek来看到这个。

use Devel::Peek;

my $s = '6.1.0.7 (build 25.3.1103030000)';
if ($s =~ /^\s*([0-9.]*) \(build.*\)/) { 
    Dump($1);
    printf STDERR "\$1 = $1\n";
    Dump($1);
}

结果是

SV = PVMG(0x1434ca4) at 0x144d83c
  REFCNT = 1
  FLAGS = (GMG,SMG)
  IV = 0
  NV = 0
  PV = 0
  MAGIC = 0x146c324
    MG_VIRTUAL = &PL_vtbl_sv
    MG_TYPE = PERL_MAGIC_sv(\0)
    MG_OBJ = 0x144d82c
    MG_LEN = 1
    MG_PTR = 0x14631c4 "1"
$1 = 6.1.0.7
SV = PVMG(0x1434ca4) at 0x144d83c
  REFCNT = 1
  FLAGS = (GMG,SMG,pPOK)
  IV = 0
  NV = 0
  PV = 0x1487a1c "6.1.0.7"\0
  CUR = 7
  LEN = 8
  MAGIC = 0x146c324
    MG_VIRTUAL = &PL_vtbl_sv
    MG_TYPE = PERL_MAGIC_sv(\0)
    MG_OBJ = 0x144d82c
    MG_LEN = 1
    MG_PTR = 0x14631c4 "1"

请注意,在第二个转储输出中,已填充PV槽(字符串值),并且已在FLAGS下添加了pPOK标记。

所以,是的,print有副作用,虽然在正常情况下你不应该注意到。 version->parse()似乎期望一个字符串参数,但不会触发字符串语义。鉴于version更喜欢使用XS实现,它可能是一个bug而不是perl。请注意,制作捕获数据的副本会导致问题消失:

use Data::Dump qw'pp';

my $s = '6.1.0.7 (build 25.3.1103030000)';
if ($s =~ /^\s*([0-9.]*) \(build.*\)/) { 
    my $x = $1;
    pp(version->parse($x));
}

结果:

bless({ original => "6.1.0.7", qv => 1, version => [6, 1, 0, 7] }, "version")

答案 1 :(得分:4)

看起来像是version :: parse中的一个错误,它没有正确地触发传入参数的魔法。

查看代码显示了CPAN和核心“版本”中的相同问题;它在处理魔法之前很久就检查了SvOK。不幸的是,这将涉及修复模块代码和perl核心。

答案 2 :(得分:3)

使用Mac版X 10.6.8上的Perl 5.14.1,使用'版本'0.91(即版本0.91)的XS实现再现......

但是,它是version::vxs(XS实现)中的错误,而不是version::vpp(纯Perl)实现。

use version::vxs;
use Data::Dumper;
my $codeLevel = q{6.1.0.7 (build 25.3.1103030000)};
print STDERR qq{$codeLevel\n};
my $vrmf;
if($codeLevel =~ /^\s*([0-9.]*) \(build.*\)/) { 
    $vrmf = version::vxs->parse($1);
}

print STDERR Dumper($vrmf);

如你所示,那失败了。当我编译纯Perl版本(perl Makefile.PL --perl_only,如README文件中所述)并对其进行测试时,它运行正常:

$ perl -Iblib/lib x4.pl
6.1.0.7 (build 25.3.1103030000)
$VAR1 = bless( {
                 'original' => '6.1.0.7',
                 'qv' => 1,
                 'version' => [
                                6,
                                1,
                                0,
                                7
                              ]
               }, 'version::vpp' );

$ cat x4.pl
use version::vpp;
use Data::Dumper;
my $codeLevel = q{6.1.0.7 (build 25.3.1103030000)};
print STDERR qq{$codeLevel\n};
my $vrmf;
if($codeLevel =~ /^\s*([0-9.]*) \(build.*\)/) { 
    $vrmf = version::vpp->parse($1);
}

print STDERR Dumper($vrmf);
$

我认为你可以理智report this on CPAN RT(作者是JPEACOCK)。解决方法是,手动安装纯Perl版本并删除XS版本(因为代码首先查找XS代码 - 请参阅version-0.91/lib/version.pm)。如果你真的很勇敢,可以考虑一下实际的bug。

答案 3 :(得分:0)

总是这样;你问一个问题,然后找到答案!

此代码:

use version;
use Data::Dumper;
my $codeLevel = q{6.1.0.7 (build 25.3.1103030000)};
print STDERR qq{$codeLevel\n};
my $vrmf;
if($codeLevel =~ /^\s*([0-9.]*) \(build.*\)/) {
    $vrmf = version->parse("$1");
}
print STDERR Dumper($vrmf);

正常工作。请注意,$ 1已在此处进行了字符串化。

然而,这留下了一个有趣的观察结果:打印一个变量似乎永久地将其字符串化了!

答案 4 :(得分:0)

我不知道版本号背后的特殊魔力,但使用overload,您几乎可以提供任何想要进行字符串化的副作用。

package My::Class;
use overload
    '""' => \&arbitrary_function;

...

$obj = My::Class->new();
print $obj;                 # invokes &arbitrary_function