【问题标题】:How do I search a Perl array for a matching string?如何在 Perl 数组中搜索匹配的字符串?
【发布时间】:2026-01-28 03:45:01
【问题描述】:

在 Perl 中通过字符串数组搜索匹配字符串的最聪明的方法是什么?

一个警告,我希望搜索不区分大小写

所以"aAa" 将在("aaa","bbb")

【问题讨论】:

  • 你会在列表中搜索多少次?
  • 实际上只会搜索一次。运行时复杂性并不是我真正担心的
  • 无关紧要,或者有任何关联,但是如果您将数组保存在一组散列键中(所有值都为“whatever”),您可以找出它是否存在或尽管不区分大小写确实会带来问题,但速度并没有快多少……哦,是的,而且 ~~ smartmatch 可能很慢……否则,请坚持使用 Ether 有据可查的答案,证明最简单的答案并不总是最好的答案,即使不是从你的角度来看,也是正确的答案。

标签: perl string search text


【解决方案1】:

对于布尔匹配结果或出现次数,您可以使用:

use 5.014; use strict; use warnings;
my @foo=('hello', 'world', 'foo', 'bar', 'hello world', 'HeLlo');
my $patterns=join(',',@foo);
for my $str (qw(quux world hello hEllO)) {
    my $count=map {m/^$str$/i} @foo;
    if ($count) {
        print "I found '$str' $count time(s) in '$patterns'\n";
    } else {
        print "I could not find '$str' in the pattern list\n"
    };
}

输出:

I could not find 'quux' in the pattern list
I found 'world' 1 time(s) in 'hello,world,foo,bar,hello world,HeLlo'
I found 'hello' 2 time(s) in 'hello,world,foo,bar,hello world,HeLlo'
I found 'hEllO' 2 time(s) in 'hello,world,foo,bar,hello world,HeLlo'

不需要使用模块。
当然,它不像上面的一些代码那样“可扩展”和通用。
我将其用于交互式用户答案,以匹配一组预定义的不区分大小写的答案。

【讨论】:

    【解决方案2】:

    这取决于您希望搜索做什么:

    • 如果要查找所有匹配项,请使用内置的grep

      my @matches = grep { /pattern/ } @list_of_strings;
      
    • 如果您想找到第一个匹配项,请在List::Util 中使用first

      use List::Util 'first';  
      my $match = first { /pattern/ } @list_of_strings;
      
    • 如果要查找所有匹配项的计数,请在List::MoreUtils 中使用true

      use List::MoreUtils 'true';
      my $count = true { /pattern/ } @list_of_strings;
      
    • 如果您想知道第一个匹配的索引,请在List::MoreUtils 中使用first_index

      use List::MoreUtils 'first_index'; 
      my $index = first_index { /pattern/ } @list_of_strings;
      
    • 如果您只想知道是否有匹配项,但您不关心它是哪个元素或它的值,请在List::Util 中使用any

      use List::Util 1.33 'any';
      my $match_found = any { /pattern/ } @list_of_strings;
      

    所有这些示例的核心都是类似的事情,但它们的实现已经过高度优化,速度很快,并且比你自己用grepmap 或 @ 编写的任何纯 perl 实现都要快987654328@.


    请注意,执行循环的算法与执行单个匹配是一个单独的问题。要不区分大小写地匹配字符串,您可以简单地在模式中使用i 标志:/pattern/i。如果您以前没有阅读过perldoc perlre,那么您绝对应该阅读。

    【讨论】:

    • 您假设“匹配”表示正则表达式匹配,但给出的示例只是(不区分大小写)相等。
    • true 有点矫枉过正。比my $count = grep { /pattern/ } @list_of_strings;快吗?
    • @Zaid 甚至是perldoc perlrequick,然后是perldoc perlreut
    • Zaid 和 Telemachus:perlretquick 和 perlretut 都在 perlre 描述的第一段中特别提到。这是最好的开始,因为它为读者提供了如何沿着学习路径进行的路线图。 (除非您处于腕管综合症的终端,或者只是讨厌多点击一次鼠标)。
    【解决方案3】:
    #!/usr/bin/env perl
    
    use strict;
    use warnings;
    use Data::Dumper;
    
    my @bar = qw(aaa bbb);
    my @foo = grep {/aAa/i} @bar;
    
    print Dumper \@foo;
    

    【讨论】:

      【解决方案4】:

      Perl 字符串匹配也可用于简单的是/否。

      my @foo=("hello", "world", "foo", "bar");
      
      if ("@foo" =~ /\bhello\b/){
          print "found";
      }
      else{
          print "not found";
      }
      

      【讨论】:

      • 这在某些情况下会导致误报,例如my @foo = ( "hello world hello bar" );
      • 对误报的良好观察。意识到这一点,我发现这对于单字测试来说既好又简单。如有必要,总是可以使用连接添加分隔符 - 例如使用 \x01 将适用于大多数文本字符串。
      【解决方案5】:

      Perl 5.10+ 包含“智能匹配”运算符~~,如果某个元素包含在数组或散列中,则返回 true,否则返回 false(参见 perlfaq4):

      好消息是它还支持正则表达式,这意味着您可以轻松处理不区分大小写的要求:

      use strict;
      use warnings;
      use 5.010;
      
      my @array  = qw/aaa bbb/;
      my $wanted = 'aAa';
      
      say "'$wanted' matches!" if /$wanted/i ~~ @array;   # Prints "'aAa' matches!"
      

      【讨论】:

      【解决方案6】:

      如果您要对数组进行多次 搜索,AND 匹配总是被定义为字符串等价,那么您可以规范化您的数据并使用哈希。 p>

      my @strings = qw( aAa Bbb cCC DDD eee );
      
      my %string_lut;
      
      # Init via slice:
      @string_lut{ map uc, @strings } = ();
      
      # or use a for loop:
      #    for my $string ( @strings ) {
      #        $string_lut{ uc($string) } = undef;
      #    }
      
      
      #Look for a string:
      
      my $search = 'AAa';
      
      print "'$string' ", 
          ( exists $string_lut{ uc $string ? "IS" : "is NOT" ),
          " in the array\n";
      

      让我强调一下,如果您打算在数组上进行多次查找,那么进行哈希查找是很好的。此外,它仅在匹配意味着 $foo eq $bar 或可以通过规范化满足的其他要求(如不区分大小写)时才有效。

      【讨论】:

        【解决方案7】:

        我猜

        @foo = ("aAa", "bbb");
        @bar = grep(/^aaa/i, @foo);
        print join ",",@bar;
        

        会成功的。

        【讨论】:

        • 也许:@bar = grep(/^aaa$/i, @foo);因为你写的会搜索所有以 /aaa/i 开头的字符串,所以它也会找到 /aaaa/ 和 /aaaa+/。
        • 我认为使用grep {lc $_ eq 'aaa'}, @foo 会更有效,从而避免了正则表达式处理的需要。
        • 全部正确,并且根据用例非常有效。但我想 OP 给出的例子对他的问题只是稍微有代表性。