是否有Perl快捷方式来计算字符串中的匹配数?


76

假设我有:

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

我应该如何处理上下文以获取模式找到匹配项的次数(3)?可以使用单线吗?

我尝试了这个:

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

我以为通过加括号$number,可以强制使用数组上下文,使用scalar可以得到计数。但是,我得到的只是1

Answers:


117

这会将正则表达式本身置于标量上下文中,这不是您想要的。相反,将正则表达式放在列表上下文中(以获取匹配数),然后将放入标量上下文中。

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

4
好吧,perlsecret确实建议使用“土星”作为备用名称。:)
oalders 2015年

1
有人可以向我解释这段代码吗?我是perl的新手,但对上下文仍然不太满意。
爱德华·加根

第一部分,() = $string =~ /\./gi使match运算符在列表上下文中返回匹配结果。这类似于my @results = $string =~ /\./gi;。接下来,该my $number部分是标量值。将列表上下文的结果分配给标量将返回其长度。这与相同my $count = @some_list,返回数组的长度。我在下面的回答是在此处可视化行为的另一种方式。
罗伯特·P

34

我认为描述此问题的最清晰方法是避免即时转换为标量。首先分配给一个数组,然后在标量上下文中使用该数组。这基本上就是该= () =成语将要执行的操作,但是没有(很少使用)成语:

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

14
+1是最直接的方式,山羊胡子运算符令人恐惧。
Matteo Riva

2
不过,括号@count是不必要的。
Matteo Riva

20

另外,请参阅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

试试这个:


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

3给我回来。通过创建对数组的引用,可以在列表上下文中评估正则表达式,并@{..}取消对数组引用的引用。


4
您不需要任何括号。
布拉德·吉尔伯特

1
我必须说我比山羊皮更喜欢这种方法。实际上,我几乎比山羊胡子都喜欢所有东西。
威克

0

我注意到,如果您在正则表达式(例如/(K..K)|(V.AK)/gi)中具有OR条件,则生成的数组可能具有未定义的元素,这些元素包括在末尾的计数中。

例如:

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

给出的计数值为6。

我在这篇文章中找到了解决方案 如何从数组中删除所有undef?

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

然后给出正确的答案三。


-1

其他方式,

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


-1

Friedo的方法是:$a = () = $b =~ $c

但是有可能将其简化为just ($a) = $b =~ $c,如下所示:

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

您只需要将其包装在函数中就可以了,getMatchCount()不必担心会破坏传递的字符串。

另一方面,您可以添加一个交换,这可能需要更多的计算,但不会导致更改字符串。

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

除了这是替代项,而不是匹配项:它将破坏原始字符串。这与@Mike六年前的想法相同。
fishinear

@fishinear:这与Mike截然不同。他能够打印它,但不能将其存储到变量中。差异是显着的。
HoldOffHunger

1
如果您需要非破坏性的,如果您喜欢危险的生活,则只需s /(regex)/ $ 1 / g或/(= regex)// g。
android.weasel

@ android.weasel哦,好点!用此评论更新。我通常将这样的东西包装在函数中,所以我自己不必担心传递的args的可破坏性(不确定哪个更快,因为现在它正在执行交换)。但这是有用的信息,补充!
HoldOffHunger
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.