Есть ли ярлык Perl для подсчета количества совпадений в строке?

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

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

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

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

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

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

Ответ 1

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

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

Ответ 2

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

my $string = "one.two.three.four";
my @count = $string =~ /\./g;
print scalar @count;

Ответ 3

Также см. 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;

Ответ 4

Попробуйте следующее:


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

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

Ответ 5

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

print $string =~ s/\./\./g;

Ответ 6

Я заметил, что если в вашем регулярном выражении есть условие ИЛИ (например, /(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";

Который затем дает правильный ответ из трех.

Ответ 7

другой способ,

my $string = "one.two.three.four";
@s = split /\./,$string;
print scalar @s - 1;

Ответ 8

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

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

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

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

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

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

Ответ 9

my $count = 0;
my $pos = -1;
while (($pos = index($string, $match, $pos+1)) > -1) {
  $count++;
}

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