我正在努力匹配其中一个
(\S+)(=)([fisuo])
或
(\S+)(!)
然后将结果放在列表中(捕获组)。我所有的尝试都会导致额外的、不需要的捕获。
这是一些代码:
#!/usr/bin/perl
#-*- cperl -*-
# $Id: test7,v 1.1 2023/04/10 02:57:12 bennett Exp bennett $
#
use strict;
use warnings;
use Data::Dumper;
foreach my $k ('debugFlags=s', 'verbose!') {
my @v;
# Below is the offensive looking code. I was hoping for a regex
# which would behave like this:
if(@v = $k =~ m/^(\S+)(=)([fisuo])$/) {
printf STDERR ("clownMatch = '$k' => %s\n\n", Dumper(\@v));
} elsif(@v = $k =~ m/^(\S+)(!)$/) {
printf STDERR ("clownMatch = '$k' => %s\n\n", Dumper(\@v));
}
@v = ();
# This is one of my failed, aspirational matches. I think I know
# WHY it fails, but I don't know how to fix it.
if(@v = $k =~ m/^(?:(\S+)(=)([fisuo]))|(?:(\S+)(!))$/) {
printf STDERR ("hopefulMatch = '$k' => %s\n\n", Dumper(\@v));
}
printf STDERR "===\n";
}
exit(0);
__END__
输出:
clownMatch = 'debugFlags=s' => $VAR1 = [
'debugFlags',
'=',
's'
];
hopefulMatch = 'debugFlags=s' => $VAR1 = [
'debugFlags',
'=',
's',
undef,
undef
];
===
clownMatch = 'verbose!' => $VAR1 = [
'verbose',
'!'
];
hopefulMatch = 'verbose!' => $VAR1 = [
undef,
undef,
undef,
'verbose',
'!'
];
===
代码注释中有更多详细信息。输出位于代码部分的底部。还有'!'性格就是这样。我没有将它与其他一些东西混淆。
在交替中,all 捕获的值被返回,即使是那些不匹配的。
一个简单的方法是从返回列表中过滤掉
undef
的
if (my @v = grep { defined } $s =~ /^(?: (\S+)(=)([fisuo]) | (\S+)(!) )$/x)
还有其他方法可以构建正则表达式,但直接交替就可以了。
我们可以使用以下单个正则表达式模式:
^(\S+)([!=])((?<==)[fisuo])?$
这表示匹配:
^
从字符串的开头(\S+)
匹配并捕获 $1
一个非空白术语([!=])
在$2
中匹配并捕获!
或=
((?<==)[fisuo])?
然后在$3
中选择性地捕获来自fisuo
的一封信
后视(?<==)
确保这只匹配=
$
字符串的结尾因为你要匹配两个不同的东西,所以有两个不同的匹配似乎是完全合理的。
但是,如果你想把它们结合起来,你可以这样做:
m{^
(\S+)
(?:
=([fisuo]) |
(!)
)
$
}x
$1 是名字。 $2 是开关,如果存在的话。 $3 是 !,如果存在的话。
对于任何更复杂的事情,使用named captures或Regexp::Assemble.
我所有的尝试都会导致额外的、不需要的捕获。
我会去“分支重置”
(?| pattern1 | pattern2 | ... )
就像@bobble_bubble已经建议的那样(仅作为评论)
这是一个通用的解决方案,可以将不同的模式与组结合起来,同时重置捕获计数。
唉,与他链接到的文档相反,您仍然会在 LIST 的末尾获得
undef
插槽,用于返回具有较少组的模式。
但是如果这真的困扰你 - 我个人会保留它们 - 你可以像@zdim建议的那样使用
grep {defined}
安全地过滤掉它们。
这是安全的,因为
undef
表示不匹配,不能与空匹配混淆 ""
.
这里是覆盖你的测试用例的代码
use v5.12.0;
use warnings;
use Data::Dump qw/pp ddx/;
use Test::More;
# https://stackoverflow.com/questions/75974097/merge-two-regexes-with-variable-number-of-capture-groups
my %wanted =
(
"debugFlags=s" => ["debugFlags", "=", "s"],
"verbose!" => ["verbose", "!"],
);
while ( my ( $str, $expect) = each %wanted ) {
my @got =
$str =~ / (\S+)
(?|
(=) ([fisuo]+)
|
(!)
)
/x;
ddx \@got; # with trailing undefs
@got = grep {defined} @got; # eliminate undefs
is_deeply( \@got, $expect, "$str => ". pp(\@got));
}
done_testing();
-->
# branchreset.pl:25: ["debugFlags", "=", "s"]
ok 1 - debugFlags=s => ["debugFlags", "=", "s"]
# branchreset.pl:25: ["verbose", "!", undef]
ok 2 - verbose! => ["verbose", "!"]
1..2
但同样,我没有看到最后消除
undef
插槽的意义,因为无论如何您都需要单独处理不同的情况。
有一天,您可能也想在分支之后添加模式。如果分支重置真的跳过了缺失的组,那将改变尾随组的编号以致面目全非。所以从设计的角度来看做得很好。