在Perl中使用双冒号进行奇数字符串解析

时间:2014-06-23 14:39:03

标签: string perl parsing colon

我偶然发现了Perl字符串解析的奇怪之处。这是一个例子:

$word = "hello";
$test1 = "colon:values::$word:test";
$test2 = "colon:values::$word::test";

// test1 prints: "colon:values::hello:test"
// test2 prints: "colon:values::"

因此,如果Perl在字符串中的变量后面看到一个双冒号,它(我假设)会认为你正在尝试使用包名。我猜它正在尝试加载“Hello :: test”而没有找到任何东西 - 因此提前终止字符串。

这是正常的吗?我觉得这很反直觉。我能够通过逃避第一个冒号来解决这个问题:

$works = "colon:values::$word\::test";

这是一个错误还是我完全错过了一些明显的东西?

3 个答案:

答案 0 :(得分:8)

虽然这有效,但处理案例的更广泛的方法是使用大括号消除变量的名称:

perl -E "my $word = 'red'; say qq|colon::values::${word}::test|"
colon::values::red::test

perldata中记录了这一点:

  

与某些shell一样,您可以将变量名称括在括号中   将其从跟随字母数字(和下划线)中消除歧义。您   在将变量插入到字符串中时,也必须这样做   将变量名称与后面的双冒号或   撇号,因为这些将被视为一个包   分离器:

$who = "Larry";
print PASSWD "${who}::0:0:Superuser:/:/bin/perl\n";
print "We use ${who}speak when ${who}'s here.\n";
     

没有大括号,Perl会寻找$whospeak,a   $who::0$who's变量。最后两个是$0和   (推测)不存在的包$s中的who变量。

答案 1 :(得分:3)

之所以发生这种情况,是因为它试图查找$word::test作为变量名称。如果您已正确推断,则包分隔符::是名称的一部分,但它不会遵循$word的值,因为它未被使用作为这里的符号引用(需要更多语法)。

Oesor的解决方案将解决您的问题 - 解决此类问题的一般方法是使用${identifier}语法。基本上在您将变量插入字符串时使用它,您需要使用有效的标识符或相关符号(即::)来跟随它。

答案 2 :(得分:1)

Perl使用名称空间用于变量名称,Perl在Perl看到::'时使用名称空间,它假定以前是包(即名称空间)名称。它类似于错误

use strict;
use warnings;    # You're using these? Aren't you?

my $foo = "bar"
print "The magic word is $foo_for_you\n";

由于下划线是变量名称的有效字符,因此Perl假定您希望变量$foo_for_you而不是$foo并附加_for_you。那么,你会认为这是 bug 功能吗?与此有何不同:

print "The magic word is $foobar\n";   # Whoops! my variable is $foo.

解决这个问题的方法是明确$foo是你的变量:

print "The magic word is " . $foo . "_for_you\n";
printf "The magic word is %s_for_you\n", $foo;
print "The magic word is ${foo}_for_you\n";

如果您有$foo::for::you(或$foo'for'you),则会出现同样的问题在这种情况下,Perl会在命名空间$you中查找名为foo::for的变量。您可以想象,您可以使用类似的解决方案:

print "The magic word is " . $foo . "::for::you\n";
printf "The magic word is %d::for::you\n", $foo;
print "The magic word is ${foo}::for::you\n";

命名空间用于帮助保持Perl模块中的变量不会修改程序中的变量。想象一下,在Perl包中调用一个函数,然后突然发现你在程序中使用的变量已被更改。

查看File::Find,您可以看到附加了包名称空间的变量($File::Find::name$File::Find::dir是两个示例)。