代码之家  ›  专栏  ›  技术社区  ›  macabeus

如何在递归正则表达式中获得匹配的组?

  •  0
  • macabeus  · 技术社区  · 5 年前

    我正在编写一个简单的正则表达式,它需要接收一对坐标和/或一个地图名称。

    例如:

    move 10 15 # should returns [[10, 15]]
    move 10 15 map # should returns [[10, 15, 'map']]
    move map # should returns [['map']]
    move 10 15 mapA mapB # should returns [[10, 15, 'mapA'], ['mapB']] 
    move 10 15 mapA mapB 33 44 # should returns [[10, 15, 'mapA'], ['mapB'], [33, 44]]
    move 10 15 mapA 33 44 mapB # should returns [[10, 15, 'mapA'], [33, 44, 'mapB']]
    

    然后,我编写了以下正则表达式:

    /
      (?(DEFINE)
         (?<coord>    (?<x>\d+)\s+(?<y>\d+) )
         (?<map>      (?<mapname>[a-zA-Z]+) )
         (?<commands> \s* (?: (?&coord) | (?&map) ) \s* (?&commands)? )
      )
      move\s+(?&commands)
    /six
    

    但我如何才能获得团队的价值 x ,则, y map 使用Perl?

    我尝试了一些方法:

    use strict;
    use warnings;
    
    my $command = 'move 10 15';
    
    $command =~ /
      (?(DEFINE)
         (?<coord>    (?<x>\d+)\s+(?<y>\d+) )
         (?<map>      (?<mapname>[a-zA-Z]+) )
         (?<commands> \s* (?: (?&coord) | (?&map) ) \s* (?&commands)? )
      )
      move\s+(?&commands)
    /six;
    
    while (my ($k,$v) = each %+) { print "$k $v\n" }
    print "$+{x}";
    
    0 回复  |  直到 5 年前
        1
  •  5
  •   zdim    5 年前

    问题是,你不能这样。这个 perlre 关于这件事

    请注意,递归返回后,无法访问在递归内部匹配的捕获组,因此需要额外的捕获组层。

    但是模式 <x> 因为它只在语法中使用,所以不能在事后进行“额外的一层”捕获。你只能拥有全部

    if ($command =~ /
            move\s+ (?<match>(?&commands))
            (?(DEFINE)
                (?<coord>    (?<x>\d+)\s+(?<y>\d+) )
                (?<map>      (?<mapname>[a-zA-Z]+) )
                (?<commands> \s* (?: (?&coord) | (?&map) ) \s* (?&commands)? )
            )
        /six)
    {
        say "got: $+{match}";
    }
    

    我把 ?(DEFINED) 按照建议,在图案的末尾进行阻挡。

    请注意,这也没有意义:在递归匹配中, 哪一个 共多个 <x> 一个人应该得到什么?因此,您需要重新调整方法,以便能够重新捕获您想要的匹配;但如果你想把一个子模式埋得这么深,我不知道该怎么做。

    对于前面提到的问题,我会编写一个简单的解析器,更不用说一个全面的正则表达式了。或者,在你的方法中,重新处理比赛的各个部分,希望一旦你有了它就会容易得多。

    还有一些强大的工具,比如 Marpa::R2 ,则, Parse::RecDescent ,则, Regexp::Grammars

        2
  •  2
  •   Stefan Becker    5 年前

    也许分而治之比把所有东西都塞进一个正则表达式更好?

    #!/usr/bin/perl
    use warnings;
    use strict;
    
    use Data::Dumper;
    
    while (<DATA>) {
        my @row;
        chomp;
        if (/^move/) {
            while (/(?:(\d+)\s+(\d+))?(?:\s+([[:alpha:]]+))?/g) {
                my @match;
                push(@match, +$1, +$2) if $1 && $2;
                push(@match, $3)       if $3;
                push(@row, \@match) if @match;
            }
        }
    
        print "$_: ", Dumper(\@row);
    }
    
    exit 0;
    
    __DATA__
    move 10 15
    move 10 15 map
    move map
    move 10 15 mapA mapB
    move 10 15 mapA mapB 33 44
    move 10 15 mapA 33 44 mapB
    

    试运行:

    $ perl dummy.pl
    move 10 15: $VAR1 = [
              [
                '10',
                '15'
              ]
            ];
    move 10 15 map: $VAR1 = [
              [
                '10',
                '15',
                'map'
              ]
            ];
    move map: $VAR1 = [
              [
                'map'
              ]
            ];
    move 10 15 mapA mapB: $VAR1 = [
              [
                '10',
                '15',
                'mapA'
              ],
              [
                'mapB'
              ]
            ];
    move 10 15 mapA mapB 33 44: $VAR1 = [
              [
                '10',
                '15',
                'mapA'
              ],
              [
                'mapB'
              ],
              [
                '33',
                '44'
              ]
            ];
    move 10 15 mapA 33 44 mapB: $VAR1 = [
              [
                '10',
                '15',
                'mapA'
              ],
              [
                '33',
                '44',
                'mapB'
              ]
            ];
    
        3
  •  2
  •   Skeeve    5 年前

    由于我还不能发表评论,斯特凡·贝克尔的解决方案有一个缺陷。

    如果坐标为0,则会失败。

    修复方法如下:

    #!/usr/bin/perl
    use warnings;
    use strict;
    
    use Data::Dumper;
    
    while (<DATA>) {
        my @row;
        chomp;
        if (/^move/) {
            while (/(?:(\d+)\s+(\d+))?(?:\s+([[:alpha:]]+))?/g) {
                my @match;
                push(@match, +$1, +$2) if defined $1 && defined $2;
                push(@match, $3)       if $3;
                push(@row, \@match) if @match;
            }
        }
    
        print "$_: ", Dumper(\@row);
    }
    
    exit 0;
    
    __DATA__
    move 10 15
    move 10 15 map
    move map
    move 10 15 mapA mapB
    move 10 15 mapA mapB 33 44
    move 10 15 mapA 33 44 mapB
    move 0 15 mapA 33 44 mapB