【问题标题】:How can I store regex captures in an array in Perl?如何将正则表达式捕获存储在 Perl 的数组中?
【发布时间】:2011-01-19 06:15:51
【问题描述】:

是否可以将正则表达式的所有匹配项存储到数组中?

我知道我可以使用($1,...,$n) = m/expr/g;,但似乎只有在您知道要查找的匹配数量的情况下才能使用它。我试过my @array = m/expr/g;,但这似乎不起作用。

【问题讨论】:

  • 解释“似乎不起作用”,最好用一个实际的例子。这应该工作。
  • 使用($1, ...) = ... 是一个非常糟糕的示例,应该进行编辑。这很令人困惑,因为捕获组已经设置了$1。那你为什么要重新分配(这是允许的)?

标签: regex perl arrays


【解决方案1】:

如果您正在进行全局匹配 (/g),则列表上下文中的正则表达式将返回所有捕获的匹配项。只需这样做:

my @matches = ( $str =~ /pa(tt)ern/g )

这个命令例如:

perl -le '@m = ( "foo12gfd2bgbg654" =~ /(\d+)/g ); print for @m'

给出输出:

12
2
654

【讨论】:

  • 如果你在 windows 'shell' 中尝试这个,一定要使用 ",像这样 perl -le "@m = ( 'foo12gfd2bgbg654' =~ /(\d+)/g ); print for @m" 否则会出错,因为 shell 使用 " 作为字符串分隔符
  • 不幸的是,它不能像稍微修改的示例中那样替代:perl -le '@m = ( (my $s = "foo12gfd2bgbg654") =~ s/(\d+)//g ); print for @m' 只是打印3
  • @U.Windl 这不是“稍微修改”。您将获得s/.../.../g 的返回值,它返回所做的替换次数。您可以在替换中使用捕获的值的唯一地方是替换值。例如:s/(\d+)/Found number $1/g.
【解决方案2】:

请参阅“列表上下文匹配”下perldoc perlop 的手动条目:

如果不使用 /g 选项,则 m// 在列表上下文中返回一个由 与模式中的括号匹配的子表达式,即 ($1 , $2 , $3 ...)

/g 修饰符指定全局模式匹配——即匹配多次 可能在字符串中。它的行为方式取决于上下文。在列表上下文中,它 返回与正则表达式中的任何捕获括号匹配的子字符串列表。如果没有括号,则返回所有匹配字符串的列表,就好像整个模式都有括号一样。

您可以通过分配给一个数组或以其他方式在列表上下文中执行评估来简单地获取所有匹配项:

my @matches = ($string =~ m/word/g);

【讨论】:

    【解决方案3】:

    有时您需要全局获取所有匹配项,就像 PHP 的 preg_match_all 那样。如果是您的情况,那么您可以编写如下内容:

    # a dummy example
    my $subject = 'Philip Fry Bender Rodriguez Turanga Leela';
    my @matches;
    push @matches, [$1, $2] while $subject =~ /(\w+) (\w+)/g;
    
    use Data::Dumper;
    print Dumper(\@matches);
    

    打印出来

    $VAR1 = [
              [
                'Philip',
                'Fry'
              ],
              [
                'Bender',
                'Rodriguez'
              ],
              [
                'Turanga',
                'Leela'
              ]
            ];
    

    【讨论】:

    • 非常方便的技术;有没有办法概括这一点,以防捕获组的数量未知?看起来您可能需要一个包含 ( $1, $2, ...) 的特殊数组变量,但我找不到这样的东西。
    • @mklement0 是的,在 Perl 5.25.7 中,添加了变量 @{^CAPTURE}。它包含上次成功匹配的($1, $2, ...)。要概括上面的答案,请执行push @matches, [@{^CAPTURE}] while $subject =~ /(\w+) (\w+)/g;
    【解决方案4】:

    我认为这是一个不言自明的例子。注意第一个正则表达式中的/g 修饰符:

    $string = "one two three four";
    
    @res = $string =~ m/(\w+)/g;
    print Dumper(@res); # @res = ("one", "two", "three", "four")
    
    @res = $string =~ m/(\w+) (\w+)/;
    print Dumper(@res); # @res = ("one", "two")
    

    请记住,您需要确保左值在列表上下文中,这意味着您必须用括号将标量值括起来:

    ($one, $two) = $string =~ m/(\w+) (\w+)/;
    

    【讨论】:

      【解决方案5】:

      是否可以将正则表达式的所有匹配项存储到数组中?

      是的,在 Perl 5.25.7 中,添加了变量 @{^CAPTURE},它保存“最后一次成功模式匹配的捕获缓冲区的内容,如果有的话”。这意味着它包含($1, $2, ...),即使捕获组的数量未知。

      在 Perl 5.25.7(自 5.6.0 起)之前,您可以使用 @Jaques 在他的回答中建议的 @-@+ 构建相同的数组。你必须这样做:

          my @capture = ();
          for (my $i = 1; $i < @+; $i++) {
              push @capture, substr $subject, $-[$i], $+[$i] - $-[$i];
          }
      

      【讨论】:

        【解决方案6】:

        我很惊讶这里没有提到这个,而是perl documentation provides 和标准变量@+。引用文档:

        此数组保存当前活动动态范围内最后成功的子匹配的开头的偏移量。

        所以,要获得第一次捕获的值,可以这样写:

        print substr( $str, $-[1], $+[1] - $-[1] ), "\n"; # equivalent to $1
        

        附带说明一下,还有一个非常漂亮的标准变量%-,因为它不仅包含命名捕获,还允许将重复名称存储在数组中。

        使用文档中提供的示例:

        /(?<A>1)(?<B>2)(?<A>3)(?<B>4)/
        

        将产生一个带有以下条目的哈希:

        $-{A}[0] : '1'
        $-{A}[1] : '3'
        $-{B}[0] : '2'
        $-{B}[1] : '4'
        

        【讨论】:

          【解决方案7】:

          请注意,如果您知道需要每个匹配的捕获组的数量,则可以使用这种简单的方法,我将其作为示例(2 个捕获组)。

          假设您有一些“数据”,例如

          my $mess = <<'IS_YOURS';
          Richard     Rich
          April           May
          Harmony             Ha\rm
          Winter           Win
          Faith     Hope
          William         Will
          Aurora     Dawn
          Joy  
          IS_YOURS
          

          使用以下正则表达式

          my $oven = qr'^(\w+)\h+(\w+)$'ma;  # skip the /a modifier if using perl < 5.14
          

          我可以在下面的@box 中捕获所有 12 个(6 对,不是 8...Harmony 逃脱,Joy 不见了)。

          my @box = $mess =~ m[$oven]g;
          

          如果我想“散列”盒子的细节,我可以这样做:

          my %hash = @box;
          

          或者我可以完全跳过这个框,

          my %hash = $mess =~ m[$oven]g;
          

          请注意,%hash 包含以下内容。订单丢失,复制密钥(如果存在的话)被压扁:

          (
                    'April'   => 'May',
                    'Richard' => 'Rich',
                    'Winter'  => 'Win',
                    'William' => 'Will', 
                    'Faith'   => 'Hope',
                    'Aurora'  => 'Dawn'
          );
          

          【讨论】:

          • 这个答案没有考虑的是捕获组的 unknown 数量:您只需使用两个。因此,您始终可以使用 ($1, $2) 来获取所有匹配项。
          猜你喜欢
          • 2021-08-06
          • 2021-09-06
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-06-02
          • 2011-01-16
          • 2018-01-15
          相关资源
          最近更新 更多