2016-05-21 28 views
1

我使用Perl 5.16.2,試圖以統計$_串特定分隔符的出現次數音譯。分隔符是通過@ARGV陣列傳遞給我的Perl程序。我確認在程序中它是正確的。我計算字符串中分隔符數量的指令是:Perl的TR操作是基於變量的名字而不是它的價值

$dlm_count = tr/$dlm//; 

如果我對分隔符進行了硬編碼,例如計數出來正確。但是當我使用變量$ 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令牌被視爲一個由四個字符組成的字符串而不是變量名稱?

回答

3

不能使用tr這種方式,它不進行內插變量。它嚴格按字符替換運行。因此,這

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

要替代每個a1,每$2,每v3。它不是一個正則表達式,但一個音譯。從perlop

因爲音譯表是在編譯時建立的,所以SEARCHLIST和REPLACEMENTLIST都不受雙引號內插。這意味着,如果你想使用的變量,你必須使用一個eval():

eval "tr/$oldlist/$newlist/"; 
die [email protected] if [email protected]; 
eval "tr/$oldlist/$newlist/, 1" or die [email protected]; 

上面的例子,從文檔的提示怎麼算。對於$dlm S IN $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的計數。

3

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

由於音譯表是在編譯時建立,既不 的SEARCHLIST也不REPLACEMENTLIST經受雙引號 插值。這意味着,如果你想使用的變量,你必須 使用一個eval():

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->(); 
    ... 
} 
1

雖然幾個答案都在eval()成語暗示爲tr///,沒有具備形成覆蓋

$_ = "abcdefghij,klm,nopqrstuvwxyz"; 

my $dlm = ","; 

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

但正如其他人指出,有一個:在字符串中有tr語法字符,EG-(連字符)的情況下重多種方法來計數的Perl避免eval()字符,這裏的另一個:

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

哇,這是響應一個金礦。感謝所有回覆的人,我非常感謝。 – rbaumann

相關問題