【问题标题】:Is there a Perl shortcut to count the number of matches in a string?是否有 Perl 快捷方式来计算字符串中的匹配数?
【发布时间】:2010-12-23 09:19:46
【问题描述】:

假设我有:

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

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

我试过了:

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

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

【问题讨论】:

    标签: arrays regex perl perl4


    【解决方案1】:

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

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

    【讨论】:

    • 好吧,perlsecret 确实建议将“Saturn”作为替代名称。 :)
    • 谁能给我解释一下这段代码?我是 perl 新手,但我仍然对上下文不太熟悉。
    • 第一部分,() = $string =~ /\./gi,使匹配运算符在列表上下文中返回匹配结果。这类似于my @results = $string =~ /\./gi;。接下来,my $number 部分是一个标量值。将列表上下文的结果分配给标量会返回其长度。这与my $count = @some_list 相同,返回数组的长度。我在下面的回答是另一种可视化行为的方式。
    【解决方案2】:

    我认为描述这一点最清晰的方法是避免立即转换为标量。首先分配给一个数组,然后在标量上下文中使用该数组。这基本上就是 = () = 成语会做的事情,但没有(很少使用的)成语:

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

    【讨论】:

    • +1 最直接的方式,goatse 算子很吓人。
    • @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】:

      下面的代码是单行代码吗?

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

      【讨论】:

        【解决方案5】:

        试试这个:

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

        它为我返回3。通过创建对数组的引用,正则表达式在列表上下文中进行评估,@{..} 取消引用数组引用。

        【讨论】:

        • 你不需要这些括号。
        • 我必须说我比山羊更喜欢这种方法。事实上,我比山羊更喜欢几乎所有东西。
        【解决方案6】:

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

        例如:

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

        给出的计数值为 6。

        我在这篇文章中找到了解决方案 How do I remove all undefs from array?

        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】:
            my $count = 0;
            my $pos = -1;
            while (($pos = index($string, $match, $pos+1)) > -1) {
              $count++;
            }
            

            用 Benchmark 查了一下,速度挺快的

            【讨论】:

            • 这不是模式匹配。
            【解决方案9】:

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

            但可以将其进一步简化为 ($a) = $b =~ $c,如下所示:

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

            感谢您将它封装在一个函数中,getMatchCount(),而不必担心它会破坏传递的字符串。

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

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

            【讨论】:

            • 除非这是替换,而不是匹配:它将破坏原始字符串。这与@Mike 6 年前的想法相同。
            • @fishinear:这与 Mike 非常不同。他能够打印它,但不能将它存储到变量中。差异很大。
            • 如果您需要非破坏性的,只要 s/(regex)/$1/g 或 /(=regex)//g 如果您喜欢危险地生活。
            • @android.weasel 哦,嘿,好点子!用那句话更新。我通常将这样的东西包装在函数中,所以我自己不必担心传递的参数的可破坏性(不确定哪个更快,因为现在它正在进行交换)。但这是有用的信息,添加!
            猜你喜欢
            • 2014-02-09
            • 2016-08-14
            • 2015-09-29
            • 2018-05-02
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-03-05
            • 2012-11-26
            相关资源
            最近更新 更多