【问题标题】:How can I store captures from a Perl regular expression into separate variables?如何将 Perl 正则表达式中的捕获存储到单独的变量中?
【发布时间】:2011-01-16 14:48:26
【问题描述】:

我有一个正则表达式:

/abc(def)ghi(jkl)mno(pqr)/igs

如何将每个括号的结果捕获到 3 个不同的变量中,每个括号一个变量?现在我使用一个数组来捕获所有结果,它们是按顺序出现的,但是我必须解析它们,而且列表可能很大。

@results = ($string =~ /abc(def)ghi(jkl)mno(pqr)/igs);

【问题讨论】:

  • 您要计算模式匹配的次数吗?这就是我的感觉......
  • 我需要处理匹配项

标签: arrays regex perl regex-group


【解决方案1】:

你的问题对我来说有点模棱两可,但我认为你想做这样的事情:

my (@first, @second, @third);
while( my ($first, $second, $third) = $string =~ /abc(def)ghi(jkl)mno(pqr)/igs) {
    push @first, $first;
    push @second, $second;
    push @third, $third;
}

【讨论】:

  • 这有点啰嗦。捕获后,您可以使用反向引用
  • ghostdog74:这是个人喜好问题。如果你真的将变量命名为 $first 和 $second,那么你也可以使用 $1 和 $2,但是如果你给它们提供更多描述性的名称,那么这样做可以提高可读性。
  • -1。我不得不同意ghostdog74;捕获到 $1 .. 系列变量在现代 Perl 中更加简洁。虽然您可以做到这一点,但这并不意味着它可能是最好的方法。
  • @leon ,是的,但是由于他无论如何都会将它们放入数组中,所以您真正关心的是数组名称。谁不知道 $1, $2 .. 是什么?
  • 很遗憾,这个答案不正确。如果$string 匹配(由于while 表达式中的列表上下文),则此答案中的while 循环将无限循环。
【解决方案2】:

您可以使用三个不同的正则表达式,每个都专注于特定的群体。显然,您只想将不同的组分配给正则表达式中的不同数组,但我认为您唯一的选择是将正则表达式拆分。

【讨论】:

    【解决方案3】:

    @OP,当括号被捕获时,你可以使用变量 $1,$2....这些是反向引用

    $string="zzzabcdefghijklmnopqrsssszzzabcdefghijklmnopqrssss";
    while ($string =~ /abc(def)ghi(jkl)mno(pqr)/isg) {
        print "$1 $2 $3\n";
    }
    

    输出

    $ perl perl.pl
    def jkl pqr
    def jkl pqr
    

    【讨论】:

    • 注意他使用了 g 修饰符。他正在进行全局匹配,所以我假设他想要存储多个匹配项。
    • 另外,$1 等不是“反向引用”,它们是捕获。然而,括号和反向引用是相关的
    【解决方案4】:

    另一种方法看起来像 ghostdog74 的答案,但使用存储哈希引用的数组:

    my @results;
    while( $string =~ /abc(def)ghi(jkl)mno(pqr)/igs) {
        my ($key1, $key2, $key3) = ($1, $2, $3);
        push @results, { 
            key1 => $key1,
            key2 => $key2,
            key3 => $key3,
        };
    }
    
    # do something with it
    
    foreach my $result (@results) {
        print "$result->{key1}, $result->{key2}, $result->{key3}\n";
    }
    

    这里的主要优点是使用单一数据结构,并且具有良好的可读循环。

    【讨论】:

      【解决方案5】:

      从 5.10 开始,您也可以使用named capture buffers

      #!/usr/bin/perl
      
      use strict; use warnings;
      
      my %data;
      
      my $s = 'abcdefghijklmnopqr';
      
      if ($s =~ /abc (?<first>def) ghi (?<second>jkl) mno (?<third>pqr)/x ) {
          push @{ $data{$_} }, $+{$_} for keys %+;
      }
      
      use Data::Dumper;
      print Dumper \%data;
      

      输出:

      $VAR1 = {
                '第一' => [
                             '定义'
                           ],
                '第二' => [
                              'jkl'
                            ],
                '第三' => [
                             'pqr'
                           ]
              };

      对于早期版本,您可以使用以下内容,以避免为每个捕获的缓冲区添加一行:

      #!/usr/bin/perl
      
      use strict; use warnings;
      
      my $s = 'abcdefghijklmnopqr';
      
      my @arrays = \ my(@first, @second, @third);
      
      if (my @captured = $s =~ /abc (def) ghi (jkl) mno (pqr) /x ) {
          push @{ $arrays[$_] }, $captured[$_] for 0 .. $#arrays;
      }
      
      use Data::Dumper;
      print Dumper @arrays;
      

      输出:

      $VAR1 = [
                '定义'
              ];
      $VAR2 = [
                'jkl'
              ];
      $VAR3 = [
                'pqr'
              ];

      但我喜欢将相关数据保存在单个数据结构中,所以最好还是使用散列。但是,这确实需要一个辅助数组:

      my %data;
      my @keys = qw( first second third );
      
      if (my @captured = $s =~ /abc (def) ghi (jkl) mno (pqr) /x ) {
          push @{ $data{$keys[$_]} }, $captured[$_] for 0 .. $#keys;
      }
      

      或者,如果变量的名称确实是 firstsecond 等,或者如果缓冲区的名称无关紧要但只有顺序,您可以使用:

      my @data;
      if ( my @captured = $s =~ /abc (def) ghi (jkl) mno (pqr) /x ) {
          push @{ $data[$_] }, $captured[$_] for 0 .. $#captured;
      }
      

      【讨论】:

      • 您只是想在第一个示例中进行深层复制吗?我只是拔出 Storable 的 dclone。要么,要么您的示例需要一些循环来构建您存储在$data 中的值。 :)
      • @brian 我正在考虑解析一个文件,其中每行给你一个first 和一个second 和一个third 值并将这些值存储在它们自己的数组中。对比 Leon Timmerman 的例子 (stackoverflow.com/questions/2259784/…)
      【解决方案6】:

      您可以编写一个包含命名捕获组的正则表达式。您可以使用捕获组开头的 ?&lt;myvar&gt; 构造来执行此操作:

      /(?<myvar>[0-9]+)/
      

      然后您可以使用$+{myvar} 表单引用那些命名的捕获组。

      这是一个人为的例子:

      perl -ne '/^systemd-(?<myvar>[^:]+)/ && { print $+{myvar} . "\n"}' /etc/passwd
      

      给定一个典型的密码文件,它会提取 systemd 用户并返回名称减去 systemd 前缀。它使用名为myvar 的捕获组。这只是一个示例,用于说明捕获组变量的使用。

      【讨论】:

        猜你喜欢
        • 2021-08-06
        • 2011-01-19
        • 1970-01-01
        • 1970-01-01
        • 2016-08-02
        • 1970-01-01
        • 1970-01-01
        • 2021-09-06
        • 1970-01-01
        相关资源
        最近更新 更多