我需要实现一个程序来计算perl中字符串中子字符串的出现次数.我已经实现如下
sub countnmstr
{
$count =0;
$count++ while $_[0] =~ /$_[1]/g;
return $count;
}
$count = countnmstr("aaa","aa");
print "$count\n";
Run Code Online (Sandbox Code Playgroud)
现在这就是我通常会做的事情.但是,在上面的实现中,我想计算'aaa'中'aa'的出现次数.在这里,我得到的回答为1似乎是合理的,但我也需要考虑重叠的情况.因此,上述情况应该给出答案为2,因为如果我们考虑重叠,则有两个'aa'.
任何人都可以建议如何实现这样的功能?
bri*_*foy 12
每个人的答案都变得非常复杂了(噢!daotoad应该把他的评论作为答案!),也许是因为他们害怕山羊运营商.我没有说出来,这正是人们所说的.它使用了一种技巧,即列表赋值的结果是右侧列表中的元素数量.
用于计算匹配的Perl习语是:
my $count = () = $_[0] =~ /($pattern)/g;
Run Code Online (Sandbox Code Playgroud)
山羊部分是= () =
,这是两个任务中间的空列表.山羊的左手部分从山羊的右侧获得计数.请注意,您需要在模式中捕获,因为这是匹配运算符将在列表上下文中返回的列表.
现在,你的下一个技巧是你真的想要一个积极的外观(或者可能是前瞻).外观不消耗字符,因此您无需跟踪位置:
my $count = () = 'aaa' =~ /((?<=a)a)/g;
Run Code Online (Sandbox Code Playgroud)
你aaa
的只是一个例子.如果您有可变宽度图案,则必须使用前瞻.Perl中的Lookbehinds必须是固定宽度.
请参阅ysth的回答 ......我没有意识到该模式可能只包含零宽度断言,并且仍然可以用于此目的.
您可以按照其他人的建议使用正向前瞻,并将函数编写为:
sub countnmstr {
my ($haystack, $needle) = @_;
my ($first, $rest) = $needle =~ /^(.)(.*)$/;
return scalar (() = $haystack =~ /(\Q$first\E(?=\Q$rest\E))/g);
}
Run Code Online (Sandbox Code Playgroud)
您还可以使用它pos
来调整下一个搜索的位置:
#!/usr/bin/perl
use strict; use warnings;
sub countnmstr {
my ($haystack, $needle) = @_;
my $adj = length($needle) - 1;
die "Search string cannot be empty!" if $adj < 0;
my $count = 0;
while ( $haystack =~ /\Q$needle/g ) {
pos $haystack -= $adj;
$count += 1;
}
return $count;
}
print countnmstr("aaa","aa"), "\n";
Run Code Online (Sandbox Code Playgroud)
输出:
C:\Temp> t 2
sub countnmstr
{
my ($string, $substr) = @_;
return scalar( () = $string =~ /(?=\Q$substr\E)/g );
}
$count = countnmstr("aaa","aa");
print "$count\n";
Run Code Online (Sandbox Code Playgroud)
几点:
//g
在列表上下文中匹配尽可能多的次数.
\Q...\E
用于自动转义任何元字符,以便您进行子字符串计数,而不是子模式计数.
使用前瞻(?= ... )
导致每个匹配不"消耗"任何字符串,允许在下一个字符处尝试以下匹配.
这使用相同的功能,其中标量上下文中的列表赋值(在本例中为空列表)返回列表赋值右侧的元素计数作为goatse/flying-lentil/spread-eagle /无论运算符,但是使用标量()而不是标量赋值来提供标量上下文.
$_[0]
不是直接使用,而是复制到词汇; 如果传递的字符串已经存储$_[0]
,$string
那么天真地使用代替将导致在//g
字符串中途而不是在开头处开始pos()
.
更新:s /// g更快,但不如使用索引快:
sub countnmstr
{
my ($string, $substr) = @_;
return scalar( $string =~ s/(?=\Q$substr\E)//g );
}
Run Code Online (Sandbox Code Playgroud)