【问题标题】:The difference between " and ' in PerlPerl 中 " 和 ' 的区别
【发布时间】:2019-10-09 22:42:16
【问题描述】:

根据this评论:

您也不应该在print ">>${ '_<$filename' }<<\n" 中使用单引号。 而是尝试:print ">>${ \"_<$filename\" }<<\n"

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

我想在这种情况下问为什么:

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

perl 打印不同的值?

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

【问题讨论】:

  • 我的猜测是因为"${ \"_&lt;$filename\" }"被解析了两次。如果你在里面使用单引号,它不会插入$filename(因为单引号不会插入)?

标签: string perl quotes double-quotes single-quotes


【解决方案1】:

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

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

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

但这不是您的代码正在做的事情。不好了。您将象征性解除引用的双管霰弹枪直接对准您的脚。您将括号的内容作为代码进行评估,并将结果用作对变量的引用。通过将全部内容放入插值字符串中,您会更加困惑。让我们不要对取消引用的概念进行不必要的深入,因为这不是这里的主要问题。关键是花括号中的内容如果不是裸词,则有效地被评估。

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

考虑到这一点,我们可以看到您的第一个示例评估为"_&lt;$filename",而您的第二个示例评估为'_&lt;$filename'。这只是插值字符串与非插值字符串之间的区别:第一个字符串将 $filename 替换为某种东西,而第二个字符串是严格字面的。

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

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

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

当然,像这样的字符串内代码和eval() 之间的主要区别在于eval() 会捕获异常并设置$@。这不会:它只是像在普通代码块中一样死掉。

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

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

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

【讨论】:

    【解决方案2】:

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

    "&gt;&gt;${ \"_&lt;$filename\" }&lt;&lt;\n" 等价于"&gt;&gt;" . ${ "_&lt;$filename" } . "&lt;&lt;\n"。

    "&gt;&gt;${ '_&lt;$filename'" }&lt;&lt;\n" 等价于"&gt;&gt;" . ${ '_&lt;$filename' } . "&lt;&lt;\n"。

    如您所见,$filename 并没有像通常在双引号字符串中那样被替换为 $filename 的值。

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

    "&gt;&gt;$h{$key}&lt;&lt;" 等价于"&gt;&gt;" . $h{$key} . "&lt;&lt;"。

    "&gt;&gt;$h{\"$key\"}&lt;&lt;" 等价于"&gt;&gt;" . $h{"$key"} . "&lt;&lt;"。

    "&gt;&gt;$h{'$key'}&lt;&lt;" 等价于"&gt;&gt;" . $h{'$key'} . "&lt;&lt;"。

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


    同样,\ 字符在字符串文字中的代码中没有特殊意义,除非它转义分隔符。

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

    如您所见,\f 并没有像双引号字符串文字中通常发生的那样被换页符替换。

    【讨论】:

      【解决方案3】:

      在这两种情况下,$filename 不是由外部字符串 ("&gt;&gt;...&lt;&lt;\n") 插入,而是由 ${ ... } 插入。 (这只是从行为中扣除)。这意味着在"&gt;&gt;${ '_&lt;$filename' }&lt;&lt;\n" 中,$filename 根本没有被插值,而在"&gt;&gt;${ \"_&lt;$filename\" }&lt;&lt;\n" 中。

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

      $filename = "ab'cd";
      

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

      如果插值是由${} 完成的,那么在"${'$filename'}" 中,${...} 实际上是${"$filename"}(没有转义的双引号),它插值$filename,你会得到类似@987654337 @,如你所愿。


      考虑这个例子:

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

      它表明,"${\"$v\"}",$v 被插值,而"${'$v'}",它不是。

      【讨论】:

      • $filename 的值为Specio::Constraint::Simple-&gt;_optimized_constraint
      • @EugenKonkov 没关系:我的示例显示为什么插值需要由${} 而不是"" 完成。 Perl 查看$filename 的值并根据该值决定需要在哪里进行插值是荒谬的。
      • 现在这个例子回答了这个问题。这是否记录了双引号字符串中的单引号切换插值?
      • @EugenKonkov 我在 perldata 和 perlop 中都没有找到任何关于此的文档。我不知道它是否记录在任何地方。
      【解决方案4】:

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

      还有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'} } );
      

      也不管它是如何被引用的。只需通过符号表,我就可以达到该值:

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

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

      1. 直接
      2. 通过符号表

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

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

      两者都会引用同一个东西。

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

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

      【讨论】:

      • 虽然这很有趣,但我看不出它如何回答您的问题,这归结为字符串中${} 中\"_&lt;$filename\" 和'_&lt;$filename' 之间的区别。在您回答的所有示例中,您在 $filename 周围使用双引号。
      • 也许它应该是您之前问题的答案?
      • @Dada:并非所有示例的文件名周围都有'。注意硬编码:Devel::Peek::Dump( ${ '::_&lt;Specio::Constraint::Simple-&gt;_optimized_constraint' } );
      • @Dada:我同意这不能直接回答我的问题。这里我展示了${ }的某些方面的测试结果。我不会接受这个答案左右。别担心。
      猜你喜欢
      • 2011-03-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-12
      • 2020-10-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多