Есть ли ярлык 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, это довольно быстро