Есть ли в Perl ярлык для подсчета количества совпадений в строке? - PullRequest
74 голосов
/ 04 декабря 2009

Предположим, у меня есть:

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

Как мне играть с контекстом, чтобы узнать, сколько раз паттерн находил совпадение (3)? Можно ли это сделать, используя однострочник?

Я пробовал это:

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

Я думал, что, поместив скобки вокруг $number, я бы принудительно установил контекст массива, а с помощью scalar я бы получил счет. Тем не менее, все, что я получаю, это 1.

Ответы [ 9 ]

112 голосов
/ 04 декабря 2009

Это ставит само регулярное выражение в скалярный контекст, а это не то, что вам нужно. Вместо этого поместите регулярное выражение в контекст списка (чтобы получить количество совпадений) и поместите , что , в скалярный контекст.

 my $number = () = $string =~ /\./gi;
32 голосов
/ 04 декабря 2009

Я думаю, что самый простой способ описать это - избежать мгновенного приведения к скаляру. Сначала присвойте массив, а затем используйте этот массив в скалярном контексте. Это в основном то, что будет делать идиома = () =, но без (редко используемой) идиомы:

my $string = "one.two.three.four";
my @count = $string =~ /\./g;
print scalar @count;
20 голосов
/ 04 декабря 2009

Также см. Perlfaq4 :

Есть несколько способов с различной эффективностью. Если вам нужно количество отдельных символов (X) в строке, вы можете использовать функцию tr /// следующим образом:

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

Это хорошо, если вы ищете только одного персонажа. Однако, если вы пытаетесь посчитать несколько символьных подстрок в большей строке, tr /// не будет работать. Что вы можете сделать, так это обернуть цикл while () вокруг глобального сопоставления с образцом. Например, давайте посчитаем отрицательные целые числа:

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

Другая версия использует глобальное совпадение в контексте списка, затем присваивает результат скаляру, производя подсчет количества совпадений.

$count = () = $string =~ /-\d+/g;
6 голосов
/ 05 декабря 2009

Является ли следующий код однострочным?

print $string =~ s/\./\./g;
6 голосов
/ 04 декабря 2009

Попробуйте это:


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

Возвращает 3 для меня. Путем создания ссылки на массив регулярное выражение оценивается в контексте списка, а @{..} отменяет ссылку на ссылку на массив.

0 голосов
/ 02 апреля 2019

Я заметил, что если в вашем регулярном выражении есть условие ИЛИ (например, /(K..K)|(V.AK)/gi), то созданный массив может содержать неопределенные элементы, которые включаются в число в конце.

Например:

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

Дает значение счетчика 6.

Я нашел решение в этом посте Как мне удалить все undefs из массива?

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";

Что дает правильный ответ на три вопроса.

0 голосов
/ 09 мая 2018
my $count = 0;
my $pos = -1;
while (($pos = index($string, $match, $pos+1)) > -1) {
  $count++;
}

проверено с помощью Benchmark, это довольно быстро

0 голосов
/ 23 октября 2015

Метод Фридона: $a = () = $b =~ $c.

Но возможно еще больше упростить это до ($a) = $b =~ $c, вот так:

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

Вы могли бы поблагодарить, просто оберните это в функцию, getMatchCount(), и не беспокойтесь о том, что это уничтожит переданную строку.

С другой стороны, вы можете добавить подкачку, которая может быть немного больше вычислений, но не приводит к изменению строки.

my ($matchcount) = $text =~ s/($findregex)/$1/gi;
0 голосов
/ 05 декабря 2009

по-другому,

my $string = "one.two.three.four";
@s = split /\./,$string;
print scalar @s - 1;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...