2016-05-21 5 views
1

Ich benutze Perl 5.16.2, um zu versuchen, die Anzahl der Vorkommen eines bestimmten Trennzeichens in der $_ Zeichenfolge zu zählen. Das Trennzeichen wird über das Array @ARGV an mein Perl-Programm übergeben. Ich überprüfe, dass es innerhalb des Programms korrekt ist. Meine Anweisung zum Zählen der Anzahl der Trennzeichen in der Zeichenkette lautet:Perl tr Operator transkribiert basierend auf dem Namen der Variable nicht ihren Wert

$dlm_count = tr/$dlm//; 

Wenn ich das Trennzeichen fest codiere, z. $dlm_count = tr/,//; die Zählung kommt richtig aus. Aber wenn ich die Variable $ dlm verwende, ist die Anzahl falsch. I modifiziert, um die Anweisung zu sagen

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

und realisiert aus, wie die Zungen in der Zeichenfolge eingefügt wurden, daß der Betrieb jeder Instanz von einem der vier Zeichen „$“ wurde Substitution „d“, „l“, oder "m" zu \t - dh eines der vier Zeichen, aus denen mein Variablenname besteht $dlm.

Hier ist ein Beispielprogramm, das das Problem veranschaulicht:

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

Es gibt nur zwei Kommas in der $_ Zeichenfolge, aber dieses Programm druckt die folgenden:

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

Warum ist die $dlm Token wird als eine literale Zeichenfolge von vier Zeichen anstatt als Variablenname behandelt?

Antwort

3

Sie nicht tr auf diese Weise verwenden können, spielt es keine Variablen interpoliert werden. Es läuft streng Zeichen für Zeichen Ersatz. Also das

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

wird jedes a mit 1 ersetzen, jedes $ mit 2 und jede v mit 3. Es ist keine Regex, sondern eine Transliteration. Von perlop

Da die Umschrift Tabelle zum Zeitpunkt der Kompilierung gebaut wird, weder die SEARCH noch die REPLACEMENTLIST werden doppelte Anführungszeichen Interpolation unterzogen. Das bedeutet, dass, wenn Sie Variablen verwenden möchten, müssen Sie eine eval():

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

Das obige Beispiel von docs Hinweise, wie man zählt.Für $dlm s in $string

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

Die $string entkommen ist so nicht interpoliert werden, bevor es eval zu bekommt. In Ihrem Fall

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

können Sie auch andere Werkzeuge als tr (oder reguläre Ausdrücke) verwenden. Zum Beispiel mit einer Schnur in $_ seine

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

Wenn split Pausen $_ durch das Muster, die leere Zeichenfolge (//) es die Liste aller Charaktere in ihr zurückkehrt. Dann testet der grep Block jeden gegen $dlm und gibt so die Liste von $dlm Zeichen zurück, wie es in $_ gab. Da dies einem Skalar zugewiesen ist, wird $dlm_count auf die Länge dieser Liste gesetzt, die die Anzahl aller $dlm ist.

3

Im Bereich der docs on perlop 'Quote Like Operators', heißt es:

Da die Umschrift Tabelle bei der Kompilierung erstellt wird, weder die SEARCH noch der REPLACEMENTLIST zu doppelte Anführungszeichen Interpolation unterworfen werden. Das bedeutet, dass, wenn Sie Variablen verwenden möchten, müssen Sie ein eval():

2

Wie dokumentiert und wie Sie festgestellt haben, interpoliert tr/// nicht. Die einfache Lösung ist stattdessen s/// zu verwenden.

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

Wenn die Umschrift in einer Schleife ausgeführt wird, wird die folgende könnte die Dinge beschleunigen spürbar:

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

Obwohl mehrere Antworten für tr/// am eval() Idiom angedeutet haben, haben keine die Form, die Abdeckungen Fälle, in denen die Zeichenfolge tr Syntax Zeichen hat, EG- (Bindestrich):

$_ = "abcdefghij,klm,nopqrstuvwxyz"; 

my $dlm = ","; 

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

Aber wie andere haben festgestellt, gibt es eine re viele Möglichkeiten, Zeichen in Perl zu zählen, die eval() vermeiden, hier ist eine andere:

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

Wow, das eine Goldmine von Antworten ist. Danke an alle, die geantwortet haben, ich schätze es sehr. – rbaumann

Verwandte Themen