Perl tr运算符基于变量的名称而不是其值进行音译

时间:2016-05-21 03:13:04

标签: perl

我正在使用Perl 5.16.2来计算$_字符串中特定分隔符的出现次数。分隔符通过@ARGV数组传递给我的Perl程序。我在程序中验证它是否正确。我计算字符串中分隔符数量的指令是:

$dlm_count = tr/$dlm//;

如果我对分隔符进行硬编码,例如$dlm_count = tr/,//;计数正确。但是当我使用变量$ dlm时,计数是错误的。我修改了说明

$dlm_count = tr/$dlm/\t/;

并实现了如何在字符串中插入选项卡,该操作替换了四个字符“$”,“d”,“l”中的任何一个的每个实例。或“m”到\t - 即组成我的变量名称$dlm的四个字符中的任何一个。

这是一个说明问题的示例程序:

$_ = "abcdefghij,klm,nopqrstuvwxyz";
my $dlm = ",";
my $dlm_count = tr/$dlm/\t/;
print "The count is $dlm_count\n";
print "The modified string is $_\n";

$_字符串中只有两个逗号,但此程序打印以下内容:

The count is 3  
The modified string is abc      efghij,k                ,nopqrstuvwxyz

为什么$dlm标记被视为四个字符的文字字符串而不是变量名?

4 个答案:

答案 0 :(得分:3)

docs on perlop 'Quote Like Operators'的部分中,它指出:

  

因为音译表是在编译时构建的,所以也不是   SEARCHLIST和REPLACEMENTLIST受到双重报价   插值。这意味着如果你想使用变量,你必须   使用eval():

答案 1 :(得分:3)

你不能以tr方式使用它,它不会插入变量。它通过字符替换严格执行。所以这个

$string =~ tr/a$v/123/

将使用a替换每个1,将$替换为2,将每个v替换为3。它不是正则表达式而是音译。来自perlop

  

由于音译表是在编译时构建的,因此SEARCHLIST和REPLACEMENTLIST都不会进行双引号插值。这意味着如果要使用变量,则必须使用eval():

eval "tr/$oldlist/$newlist/";
die $@ if $@;
eval "tr/$oldlist/$newlist/, 1" or die $@;

上述文档示例提示如何计算。对于$dlm

中的$string
$dlm_count = eval "\$string =~ tr/$dlm//";  

$string已转义,因此在到达eval之前不会进行插值。在你的情况下

$dlm_count = eval "tr/$dlm//";

您还可以使用tr(或正则表达式)以外的工具。例如,字符串位于$_

my $dlm_count = grep { /$dlm/ } split //;

split以空字符串($_)的模式中断//时,它会返回其中所有字符的列表。然后,grep块会针对$dlm对每个$dlm进行测试,因此会返回$_$dlm_count$dlm个字符的列表。由于这被分配给标量,class Something { public: void start() { this->task_ = std::thread(&Something::someTask, this); // this->task_.detach(); <<<<<< Don't do that. } void stop() { this->isRunning_ = false; task_.join(); // <<< Instead of detaching the thread, join() it. } ~Something() { this->stop(); } private: std::atomic<bool> isRunning_; std::thread task_; void someTask() { this->isRunning_ = true; while(this->isRunning_) { // do something forever } } }; 被设置为该列表的长度,这是所有std::thread的计数。

答案 2 :(得分:2)

正如文档和您发现的那样,tr///没有内插。简单的解决方案是使用s///代替。

my $dlm = ",";
$_ = "abcdefghij,klm,nopqrstuvwxyz";
my $dlm_count = s/\Q$dlm/\t/g;

如果在循环中执行音译,以下内容可能会显着加快速度:

my $dlm = ",";
my $tr = eval "sub { tr/\Q$dlm\E/\\t/ }";
for (...) {
   my $dlm_count = $tr->();
   ...
}

答案 3 :(得分:1)

尽管有几个答案暗示了eval() tr///的成语,但没有一个能够涵盖字符串中包含tr个语法字符的情况,例如 - (连字符):

$_ = "abcdefghij,klm,nopqrstuvwxyz";

my $dlm = ",";

my $dlm_count = eval sprintf "tr/%s/%s/", map quotemeta, $dlm, "\t";

但正如其他人所指出的那样,有很多方法可以计算Perl中避免eval()的字符,这是另一种:

my $dlm_count = () = m/$dlm/go;