首页 > 解决方案 > 重复匹配组时,某些捕获组似乎丢失了

问题描述

尝试解析监控插件的输出时遇到了一个问题,即匹配结果出乎我的意料:

首先考虑使用 Perl 5.18.2 的调试器会话:

 DB<6> x $_
0  'last=0.508798;;;0'
  DB<7> x $RE
0  (?^u:^((?^u:\'[^\'=]+\'|[^\'= ]+))=((?^u:\\d+(?:\\.\\d*)?|\\.\\d+))(s|%|[KMT]?B)?(;(?^u:\\d+(?:\\.\\d*)?|\\.\\d+)?){0,4}$)
   -> qr/(?^u:^((?^u:'[^'=]+'|[^'= ]+))=((?^u:\d+(?:\.\d*)?|\.\d+))(s|%|[KMT]?B)?(;(?^u:\d+(?:\.\d*)?|\.\d+)?){0,4}$)/
  DB<8> @m = /$RE/

  DB<9> x @m
0  'last'
1  0.508798
2  undef
3  ';0'
  DB<10>

好的,正则表达式$RE(旨在匹配“'label'=value[UOM];[warn];[crit];[min];[max]”)乍一看看起来很可怕,所以让我展示一下它的构造:

my $RE_label = qr/'[^'=]+'|[^'= ]+/;
my $RE_simple_float = qr/\d+(?:\.\d*)?|\.\d+/;
my $RE_numeric = qr/[-+]?$RE_simple_float(?:[eE][-+]?\d+)?/;
my $RE = qr/^($RE_label)=($RE_simple_float)(s|%|[KMT]?B)?(;$RE_simple_float?){0,4}$/;

相关部分(;$RE_simple_float?){0,4}$旨在匹配 ";[warn];[crit];[min];[max]" (仍然不完美),所以对于 ";;;0" 我希望@m';', ';', ';0'. 然而,除了最后一场比赛外,似乎比赛都输了。

我误解了什么,还是 Perl 错误?

标签: regexperlcapture-group

解决方案


当您在捕获组之后使用{<number>}(或+就此*而言)时,仅存储与捕获组匹配的最后一个值。这解释了为什么您只最终;0而不是;;;0在您的第四个捕获组中:(;$RE_simple_float?){0,4}将第四个捕获组设置为它匹配的最后一个元素。

顶级修复,我建议匹配字符串的整个结尾,然后将其拆分:

my $RE = qr/...((?:;$RE_simple_float?){0,4})$/;
my @m = /$RE/;
my @end = split /;/, $m[3]; # use /(?<=;)/ to keep the semicolons

另一种解决方案是重复捕获组:替换(;$RE_simple_float?){0,4}

(;$RE_simple_float?)?(;$RE_simple_float?)?(;$RE_simple_float?)?(;$RE_simple_float?)?

不匹配的捕获组将设置为undef。这种方法的问题在于它有点冗长,并且仅适用于{},但不适用于+or *


推荐阅读