Perl 中 " 和 ' 的区别

The difference between " and ' in Perl

根据 this 评论:

You also should not use single quotes in print ">>${ '_<$filename' }<<\n". Instead try: print ">>${ \"_<$filename\" }<<\n"

我一直认为"'之间的区别只是字符串是否被解释。

我想问一下为什么会出现这样的情况:

print ">>${ \"_<$filename\" }<<\n"
print ">>${  '_<$filename'  }<<\n"

perl 打印不同的值?

为什么我应该在这里使用 \" 而不是 '

发生的情况是,在这两种情况下,$filename 都不是由外部字符串 (">>...<<\n") 插入的,而是由 ${ ... } 插入的。 (这只是从行为中推论出来的)。这意味着在 ">>${ '_<$filename' }<<\n" 中,$filename 根本没有插值,而在 ">>${ \"_<$filename\" }<<\n".

如果它是由外部字符串插入的,那么如果您的字符串包含引号,您会 运行 遇到一些麻烦:

$filename = "ab'cd";

如果插值是由外部字符串完成的,那么"${'$filename'}" 将等同于"${'ab'cd'}",这是一个语法错误。或者更确切地说 "${ab'cd}",相当于 "${ab::cd}";也不是你想要的。

而如果插值是由 ${} 完成的,那么在 "${'$filename'}" 中,${...} 实际上是 ${"$filename"}(没有转义的 double-quotes),这插入 $filename,你会得到类似 ${"ab'cd"} 的东西,就像你想要的那样。


考虑这个例子:

$abcd = "First var";
${'$v'} = "Second var";
$v = "abcd";

say "${\"$v\"}"; # prints "First var"
say "${'$v'}";   # prints "Second var"

它表明 "${\"$v\"}" 中插入了 $v,而 "${'$v'}" 中没有插入。

这只是我的假设,我找不到任何关于此的官方文档,但它似乎可行。

另外 ilmari 说我应该 go via the symbol table:

${$main::{'_<foo::bar::baz'}}

因此我的代码分为两种情况:.

案例 1:

Devel::Peek::Dump( ${       "::_<$filename"          } );
Devel::Peek::Dump( ${       '::_<' ."$filename"      } );
Devel::Peek::Dump( ${       "::_<Specio::Constraint::Simple->_optimized_constraint"  } );
Devel::Peek::Dump( ${       '::_<Specio::Constraint::Simple->_optimized_constraint'  } );

这些都是一样的。双引号或单引号:无所谓。

SV = PV(0xe64ab00) at 0xe300bc8
  REFCNT = 1
  FLAGS = ()
  PV = 0

案例 2:

Devel::Peek::Dump( ${ ${ 'main::' }{ "_<$filename" } } );
Devel::Peek::Dump( ${       ${'::'}{ "_<$filename" } } );
Devel::Peek::Dump( ${           $::{ "_<$filename" } } );
Devel::Peek::Dump( ${     $::{"_<Specio::Constraint::Simple->_optimized_constraint"} } );
Devel::Peek::Dump( ${     $::{'_<Specio::Constraint::Simple->_optimized_constraint'} } );

也不管它是怎么引用的。只需通过符号 table 我可以达到该值:

SV = PV(0x15e0a40) at 0x186cd10
  REFCNT = 1
  FLAGS = (POK,pPOK)
  PV = 0x1934880 "Specio::Constraint::Simple->_optimized_constraint"[=14=]
  CUR = 49
  LEN = 51

调查结果
它已经不管如何引用变量名了。唯一不同的是我们的方式:

  1. 直接
  2. 通过符号 table

因此,如果您的变量名 没有 双冒号,那么您可以使用以下任一形式:

    Devel::Peek::Dump( ${ $::{ "$name" } } );
    Devel::Peek::Dump( ${    "::$name"   } );

两者指的是同一件事。

但是如果你的变量名双冒号,那么你必须只能通过符号[=]访问它的值 63=]

UPD
有人可能会注意到这个答案并没有直接回答我的问题。你是对的。 我 post 在这里测试了两种访问变量值的不同方法的结果,因为我认为这些发现很重要。

对您的问题的简短回答是 $filename 在您的第一个示例中进行了插值,而在第二个示例中则没有。你为什么问?好家伙。系好安全带,开始颠簸之旅。

首先,了解 ${...} 符号的用途。使用它有两个原因:一个是为了简单地将变量名与周围的文本区分开来;另一种是取消引用 BLOCK(代码)返回的值。

如果括号中的内容只是一个裸词(周围可能有空格),那么它只是一个简单的变量名。因此字符串 "$foo" 和字符串 "${foo}" 以及字符串 "${ foo }" 都产生相同的结果。如果您需要在字符串中的变量后直接附加文本,这会很方便:您可以说 "${foo}bar" 而不是 $foo.'bar'。这些符号也可以在插入字符串之外使用:如果你愿意,你可以说 ${foo} = 'bar'

但这不是您的代码所做的。不好了。您正将 double-barrel 符号取消引用的霰弹枪直接对准您的脚。您将括号中的内容作为代码求值,并将结果用作对变量的引用。你把所有的东西都放在一个内插字符串中,使它变得更加混乱。让我们不要对取消引用的概念进行不必要的深入讨论,因为这不是这里的主要问题。关键是,如果大括号中的内容不是裸词,则它会被有效地求值。

现在,了解当 Perl 到达您的内插字符串中的“${”符号时,它必须决定它在这里内插的是什么:一个普通的变量名或代码。 在不插入任何其他内容的情况下, 它会寻找匹配项 close-bracket。如果由这些括号分隔的 sub-string 看起来不像裸词,那么它将被评估。您仍然需要在该区域转义 double-quotes ,因为任何未转义的双引号都将算作外部字符串的最终定界符,这是其子集,并且 "block" 的其余部分将在字符串。

考虑到这一点,我们可以看到您的第一个示例求值 "_<$filename",而您的第二个示例求值 '_<$filename'。这只是内插字符串与非内插字符串之间的区别:第一个 $filename 被某些东西替换,而第二个是严格的文字。

在这两种情况下,该字符串都用于对名称可怕的全局标量变量执行符号解引用,如果您实际定义了它,那将是您的耻辱。越少说越好

如果您认为我在开玩笑说花括号的内容被有效地求值,请尝试此代码。

print "This, ${die qq(oops!\n)}, and this.\n";

当然,像这样的 in-string 代码与 eval() 的主要区别在于 eval() 会捕获异常并设置 $@。这不会:它只是像在普通代码块中一样死掉。

这引发了 Perl 的隐藏技巧之一。您不能像 $foo->bar 那样插入方法调用,但是您可以取消引用对包含它的数组的引用,就像这样。

print "@{[$foo->bar]}\n";

总而言之,看在上帝的份上,请不要写那样的代码。

答案很简单:$@ 字符在字符串文字的代码中没有特殊意义。

">>${ \"_<$filename\" }<<\n" 等同于 ">>" . ${ "_<$filename" } . "<<\n".

">>${ '_<$filename'" }<<\n" 等价于 ">>" . ${ '_<$filename' } . "<<\n".

如您所见,$filename 并未替换为 $filename 的值,这通常发生在 double-quoted 字符串中。

如果还不清楚,让我们考虑一个更常见的例子。

">>$h{$key}<<" 等价于 ">>" . $h{$key} . "<<".

">>$h{\"$key\"}<<" 等同于 ">>" . $h{"$key"} . "<<".

">>$h{'$key'}<<" 等同于 ">>" . $h{'$key'} . "<<".

现在考虑一下如果 $key 被插值会发生什么。 (例如,使用 $key = "2+3";。)这种行为会更出乎意料,也更危险。


类似地,\ 字符在字符串文字的代码中没有特殊意义,除非它转义了定界符。

"foo ${\f()} bar" 等同于 "foo " . ${\f()} . " bar"

如您所见,\f 没有像 double-quoted 字符串文字中通常发生的那样用换页替换。