我在试着匹配
(\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',
'!'
];
===
在代码注解中有更多的细节。输出在代码部分的底部。'!'字符就是这样。我没有把它和其他的not混淆。
更新于2023年4月10日星期一23:15:40 PDT:
在几位读者的明智投入下,这个问题似乎分解成了几个更小的问题。
正则表达式是否可以返回可变数量的捕获组?
我还没听说过一种或另一种方式。
如果可以的话,应该以这种方式使用正则表达式吗?
除非有个令人信服的理由
对于我的目的,我应该使用正则表达式来创建真正的词法分析器/解析器吗?
没有。我当时在用正则表达式进行语法检查,有点忘乎所以了。
不过,我学到了很多东西,我希望版主们能把这篇文章当作一个警示故事。
每个人都应该在这一点上得到分数,并且可以引用这一段来声称他们被抢劫了。
4条答案
按热度按时间omqzjyyz1#
在一个交替中,all 捕获的值被返回,即使是那些不匹配的捕获。
一个简单的方法是从返回列表中过滤掉
undef
。也有其他的方法来构建正则表达式,但是直接的交替就可以了。
这个问题特别询问了如何将两个(可选的)正则表达式模式合并为一个,以便只捕获实际匹配的内容,而不需要额外的
undef
。在我看来,这是一个很好的问题,因为它通常不需要清理。通常的交替(
p1 | p2
)返回(在alist上下文中或在@{^CAPTURE}
中)all 指示的捕获组,如上所述。如果p1
定义了三个捕获组,p2
定义了两个,最终我们得到五个;捕获匹配的分支,undef
s捕获另一个分支。简而言之,我发现要获得一组“干净”的真捕获,我们需要用语法解析pure-regex。虽然内置支持(参见DEFINE)只能匹配(“识别”)模式,但Regexp::Grammars支持的模式要多得多。一个简单的例子很合适
这个打印
结果没有排序,因此可能需要为散列添加所需的排序标准,或者遍历单个散列元素。
问题中的例子非常简单,所以一个简单的语法就可以了,但是如果我们想象它能更全面地处理选项,那么语法就需要更复杂/结构化。
它可以更容易地扩展。请参阅(似乎无穷无尽的)文档的许多许多功能。
所有其他的似乎都受到了额外的
undef
s的影响。“* 分支reset*”很接近,但仍然返回最长的一组指示的捕获组(这里是3),即使它匹配一个较短的分支,正如我在下面的评论中提到的;所以我们又得到了undef
s。关于如何使用这个,请参阅@LanX的答案。条件表达式(我希望它能躲过这一劫)也设置了它看到的所有捕获括号
在第二个测试中,我们及时地得到了两个
undef
。我使用了一个lookahead的条件,特别是为了避免额外的捕获组,但是当然,表达式中更远的 all 括号会得到它们,而不管哪些匹配。所以我们真的可以这样做ecr0jaav2#
我们可以使用以下正则表达式模式:
这说匹配:
^
从字符串的开始(\S+)
在$1
中匹配并捕获非空白项([!=])
在$2
中匹配和捕获!
或=
((?<==)[fisuo])?
然后可选地在$3
中捕获来自fisuo
的字母,(?<==)
的lookbehind确保这仅与=
匹配$
字符串结尾∮ ∮ ∮ ∮
rsaldnfx3#
我所有的尝试都会导致额外的、不必要的捕获。
我会选择“分支重置”
(?| pattern1 | pattern2 | ... )
,就像@bobble_bubble已经建议的那样(仅作为评论)这是一个通用的解决方案,将不同的模式与组组合在一起,同时重置捕获计数。
遗憾的是,与他所链接的文档相反,对于组较少的模式,您仍然会在返回的列表末尾获得
undef
插槽。但如果这真的困扰你-我个人会保留它们-你可以安全地用
grep {defined}
过滤掉它们,就像@zdim建议的那样。这是安全的,因为
undef
意味着不匹配,不能与空匹配""
混淆。这里的代码覆盖了你的测试用例
策略更新
但是,我也不认为在最后消除
undef
插槽有什么意义,因为无论如何您都需要单独处理不同的情况。有一天你可能也想在分支后添加模式。如果分支重置真的跳过了缺失的组,那将使尾随组的编号变得无法识别。所以从设计的Angular 来看,这做得很好。
vngu2lb84#
既然你要匹配两个不同的东西,那么有两个不同的匹配似乎是完全合理的。
但是,如果你想把它们结合起来,你可以这样做:
$1是名称。$2是开关(如果存在)。$3是!(如果存在)。
对于更复杂的内容,请使用命名捕获或Regexp::Assemble。
Demonstration