Gibt es eine Perl-Verknüpfung, um die Anzahl der Übereinstimmungen in einer Zeichenfolge zu zählen?

78

Angenommen, ich habe:

my $string = "one.two.three.four";

Wie soll ich mit dem Kontext spielen, um zu ermitteln, wie oft das Muster eine Übereinstimmung gefunden hat (3)? Kann dies mit einem Einzeiler erfolgen?

Ich habe es versucht:

my ($number) = scalar($string=~/\./gi);

Ich dachte, wenn ich Klammern setze $number, würde ich den Array-Kontext erzwingen und durch die Verwendung von scalardie Anzahl erhalten. Ich bekomme jedoch nur 1.

Geo
quelle

Antworten:

119

Das versetzt den regulären Ausdruck selbst in einen skalaren Kontext, was nicht das ist, was Sie wollen. Stattdessen die Regex in Listenkontext setzen (um die Anzahl der Spiele zu bekommen) und setzt die in Skalarkontext.

 my $number = () = $string =~ /\./gi;
Friedo
quelle
4
Nun, Perlsecret schlägt "Saturn" als alternativen Namen vor. :)
oalders
1
Kann mir jemand diesen Code erklären? Ich bin neu in Perl und ich bin immer noch nicht wirklich vertraut mit Kontexten.
Edward Gargan
Im ersten Teil muss () = $string =~ /\./gider Übereinstimmungsoperator die Ergebnisse der Übereinstimmung in einem Listenkontext zurückgeben. Dies ist ähnlich wie my @results = $string =~ /\./gi;. Als nächstes ist das my $numberTeil ein Skalarwert. Wenn Sie die Ergebnisse des Listenkontexts einem Skalar zuweisen, wird dessen Länge zurückgegeben. Dies ist dasselbe wie my $count = @some_list, was die Länge des Arrays zurückgibt. Meine Antwort unten ist eine andere Möglichkeit, das Verhalten hier zu visualisieren.
Robert P
35

Ich denke, der klarste Weg, dies zu beschreiben, wäre, die sofortige Umwandlung in Skalar zu vermeiden. Weisen Sie zuerst ein Array zu und verwenden Sie dieses Array dann im skalaren Kontext. Das ist im Grunde das, was die = () =Redewendung tun wird, aber ohne die (selten verwendete) Redewendung:

my $string = "one.two.three.four";
my @count = $string =~ /\./g;
print scalar @count;
Robert P.
quelle
15
+1 für den einfachsten Weg, Ziegenoperator ist beängstigend.
Matteo Riva
2
Klammern @countsind jedoch nicht erforderlich.
Matteo Riva
21

Siehe auch Perlfaq4 :

Es gibt verschiedene Möglichkeiten mit unterschiedlicher Effizienz. Wenn Sie eine Anzahl einzelner Zeichen (X) innerhalb einer Zeichenfolge zählen möchten, können Sie die Funktion tr /// folgendermaßen verwenden:

$string = "ThisXlineXhasXsomeXx'sXinXit";
$count = ($string =~ tr/X//);
print "There are $count X characters in the string";

Dies ist in Ordnung, wenn Sie nur nach einem einzelnen Zeichen suchen. Wenn Sie jedoch versuchen, Teilzeichenfolgen mit mehreren Zeichen in einer größeren Zeichenfolge zu zählen, funktioniert tr /// nicht. Sie können eine while () - Schleife um eine globale Musterübereinstimmung wickeln. Zählen wir zum Beispiel negative ganze Zahlen:

$string = "-9 55 48 -2 23 -76 4 14 -44";
while ($string =~ /-\d+/g) { $count++ }
print "There are $count negative numbers in the string";

Eine andere Version verwendet eine globale Übereinstimmung im Listenkontext und weist das Ergebnis dann einem Skalar zu, wodurch die Anzahl der Übereinstimmungen gezählt wird.

$count = () = $string =~ /-\d+/g;
Robert P.
quelle
9

Ist der folgende Code ein Einzeiler?

print $string =~ s/\./\./g;
Mike
quelle
6

Versuche dies:


my $string = "one.two.three.four";
my ($number) = scalar( @{[ $string=~/\./gi ]} );

Es kehrt 3für mich zurück. Durch Erstellen einer Referenz auf ein Array wird der reguläre Ausdruck im Listenkontext ausgewertet und die @{..}Referenzierung der Array-Referenz aufgehoben.

PP.
quelle
4
Sie brauchen keine dieser Klammern.
Brad Gilbert
1
Ich muss sagen, ich mag diese Methode besser als Ziege. Tatsächlich mag ich so ziemlich alles besser als Ziege.
Wick
0

Ich habe festgestellt, dass, wenn Sie eine ODER-Bedingung in Ihrem regulären Ausdruck haben (z. B. /(K..K)|(V.AK)/gi), das erzeugte Array möglicherweise undefinierte Elemente enthält, die am Ende in der Zählung enthalten sind.

Zum Beispiel:

my $seq = "TSYCSKSNKRCRRKYGDDDDWWRSQYTTYCSCYTGKSGKTKGGDSCDAYYEAYGKSGKTKGGRNNR";
my $regex = '(K..K)|(V.AK)';
my $count = () = $seq =~ /$regex/gi;
print "$count\n";

Gibt einen Wert von 6 an.

Ich habe die Lösung in diesem Beitrag gefunden. Wie entferne ich alle Undefs aus dem Array?

my $seq = "TSYCSKSNKRCRRKYGDDDDWWRSQYTTYCSCYTGKSGKTKGGDSCDAYYEAYGKSGKTKGGRNNR";
my $regex = '(K..K)|(V.AK)';
my @count = $seq =~ /$regex/gi;
@count = grep defined, @count; 
my $count = scalar @count;
print "$count\n";

Was dann die richtige Antwort von drei gibt.

Alastair Skeffington
quelle
-1

ein anderer Weg,

my $string = "one.two.three.four";
@s = split /\./,$string;
print scalar @s - 1;
Ghostdog74
quelle
-1
my $count = 0;
my $pos = -1;
while (($pos = index($string, $match, $pos+1)) > -1) {
  $count++;
}

mit Benchmark überprüft, ist es ziemlich schnell

Tim Cadell
quelle
Das ist keine Musterübereinstimmung.
Jim Balter
-1

Friedos Methode ist : $a = () = $b =~ $c.

Aber es ist möglich, dies noch weiter zu vereinfachen ($a) = $b =~ $c:

my ($matchcount) = $text =~ s/$findregex/ /gi;

Sie können sich dafür bedanken, dass Sie dies einfach in eine Funktion einwickeln getMatchCount()und sich keine Sorgen machen, dass die übergebene Zeichenfolge dadurch zerstört wird.

Auf der anderen Seite können Sie einen Swap hinzufügen, was zwar etwas aufwendiger ist, aber nicht zu einer Änderung der Zeichenfolge führt.

my ($matchcount) = $text =~ s/($findregex)/$1/gi;
HoldOffHunger
quelle
Abgesehen davon, dass dies eine Ersetzung ist, keine Übereinstimmung: Es wird die ursprüngliche Zeichenfolge zerstört. Und es ist die gleiche Idee wie bei @Mike vor 6 Jahren.
fishinear
@fishinear: Das ist ganz anders als Mike. Er war in der Lage, es zu drucken, aber nicht in einer Variablen zu speichern. Der Unterschied ist signifikant.
HoldOffHunger
1
Wenn Sie zerstörungsfrei brauchen, nur s / (Regex) / $ 1 / g oder / (= Regex) // g, wenn Sie gerne gefährlich leben.
android.weasel
@ android.weasel Oh, hey, guter Punkt! Aktualisierung mit dieser Bemerkung. Normalerweise verpacke ich solche Dinge in Funktionen, sodass ich mir keine Sorgen um die Zerstörbarkeit übergebener Argumente machen muss (nicht sicher, welche schneller sind, da jetzt ein Tausch durchgeführt wird). Aber das sind nützliche Informationen, die hinzugefügt werden!
HoldOffHunger