Czy istnieje skrót Perla do zliczania liczby dopasowań w łańcuchu?

Załóżmy, że mam:

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

Jak powinienem grać z kontekstem, aby uzyskać liczbę razy wzór znalazł dopasowanie (3)? Czy można to zrobić za pomocą jednego linera?

Próbowałem tego:

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

Myślałem, że umieszczając nawiasy wokół $number, wymuszę kontekst tablicy, a używając scalar, uzyskam liczbę. Jednak wszystko co dostaję to 1.

Author: Relequestual, 2009-12-04

8 answers

To umieszcza regex w kontekście skalarnym, co nie jest tym, czego chcesz. Zamiast tego, umieść Wyrażenie regularne w kontekście listy (aby uzyskać liczbę dopasowań) i umieść to w kontekście skalarnym.

 my $number = () = $string =~ /\./gi;
 108
Author: friedo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-21 19:20:03

Myślę, że najlepszym sposobem na opisanie tego byłoby uniknięcie natychmiastowego oddania do skalara. Najpierw Przypisz do tablicy, a następnie użyj tej tablicy w kontekście skalarnym. W zasadzie to samo zrobi idiom = () =, ale bez (rzadko używanego) idiomu:

my $string = "one.two.three.four";
my @count = $string =~ /\./g;
print scalar @count;
 31
Author: Robert P,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-04 22:58:27

Zobacz też Perlfaq4:

Istnieje wiele sposobów, z różną skutecznością. Jeśli chcesz policzyć pewien pojedynczy znak (X) w łańcuchu, możesz użyć funkcji tr/// w następujący sposób:

$string = "ThisXlineXhasXsomeXx'sXinXit";
$count = ($string =~ tr/X//);
print "There are $count X characters in the string";
Jest to w porządku, jeśli szukasz tylko jednego znaku. Jeśli jednak próbujesz zliczyć wiele podciągów znaków w większym łańcuchu, tr / / / nie będzie działać. Możesz zawinąć pętlę while () wokół globalnego dopasowania wzorca. Na przykład, Policzmy liczby całkowite ujemne:
$string = "-9 55 48 -2 23 -76 4 14 -44";
while ($string =~ /-\d+/g) { $count++ }
print "There are $count negative numbers in the string";

Inna wersja używa globalnego dopasowania w kontekście listy, a następnie przypisuje wynik skalarowi, tworząc liczbę dopasowań.

$count = () = $string =~ /-\d+/g;
 20
Author: Robert P,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-04 20:20:42

Spróbuj tego:


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

Zwraca 3 dla mnie. Poprzez utworzenie referencji do tablicy Wyrażenie regularne jest obliczane w kontekście listy, a @{..} odsyła do tablicy.

 6
Author: PP.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-04 20:08:52

Czy poniższy kod jest jednowierszowy?

print $string =~ s/\./\./g;
 6
Author: Mike,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-05 01:07:52

Another way,

my $string = "one.two.three.four";
@s = split /\./,$string;
print scalar @s - 1;
 -1
Author: ghostdog74,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-04 23:56:44
my $count = 0;
my $pos = -1;
while (($pos = index($string, $match, $pos+1)) > -1) {
  $count++;
}

Sprawdzone przez Benchmark, to dość szybko

 -1
Author: Tim Cadell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-09 00:10:44

Metoda Friedo to: $a = () = $b =~ $c.

Ale można to jeszcze bardziej uprościć do tylko ($a) = $b =~ $c, w ten sposób :

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

Możesz po prostu owinąć to w funkcję, getMatchCount(), i nie martwić się o to, że zniszczy przekazany ciąg.

Z drugiej strony, możesz dodać swap, który może być nieco bardziej obliczeniowy, ale nie powoduje zmiany ciągu znaków.

my ($matchcount) = $text =~ s/($findregex)/$1/gi;
 -1
Author: HoldOffHunger,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-19 16:19:26