如何在递归正则表达式中匹配一个组?
How to get a group matched in a recursive regular expression?
我正在编写一个简单的正则表达式,需要接收一对坐标 and/or 一个地图名称。
例如:
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
但是如何使用 Perl 获取组 x
、y
和 map
的值?
我尝试了一些方法:
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}";
也许分而治之比强制将所有内容都输入一个正则表达式更好?
#!/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, +, +) if && ;
push(@match, ) if ;
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'
]
];
就目前的问题而言,你不能拥有它。 perlre 说的是这个
Note that capture groups matched inside of recursion are not accessible after the recursion returns, so the extra layer of capturing groups is necessary.
但模式 <x>
不能与事后捕获的 "an extra layer" 一起使用,因为它仅在语法内部使用。你只能拥有全部
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。
由于我还不能发表评论,Stefan Becker 的解决方案存在缺陷。
坐标为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, +, +) if defined && defined ;
push(@match, ) if ;
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
我正在编写一个简单的正则表达式,需要接收一对坐标 and/or 一个地图名称。
例如:
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
但是如何使用 Perl 获取组 x
、y
和 map
的值?
我尝试了一些方法:
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}";
也许分而治之比强制将所有内容都输入一个正则表达式更好?
#!/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, +, +) if && ;
push(@match, ) if ;
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'
]
];
就目前的问题而言,你不能拥有它。 perlre 说的是这个
Note that capture groups matched inside of recursion are not accessible after the recursion returns, so the extra layer of capturing groups is necessary.
但模式 <x>
不能与事后捕获的 "an extra layer" 一起使用,因为它仅在语法内部使用。你只能拥有全部
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。
由于我还不能发表评论,Stefan Becker 的解决方案存在缺陷。
坐标为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, +, +) if defined && defined ;
push(@match, ) if ;
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