重复匹配组时,一些捕获组似乎丢失

Some capture groups seem lost when matching group repeatedly

试图将 output of monitoring plugins 我 运行 解析为匹配结果出乎我意料的问题:

首先考虑使用 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 的错误?

当您在捕获组之后使用 {<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。这种方法的问题在于它有点冗长,并且仅适用于 {},但不适用于 +*.

以下演示代码利用 split 获取感兴趣的数据。调查它是否适合作为您问题的解决方案。

use strict;
use warnings;
use feature 'say';

use Data::Dumper;

while( <DATA> ) {
    chomp;
    say;
    my $record;
    $record->@{qw/label value warn crit min max/} = split(/[=;]/,$_);
    say Dumper($record);
}

exit 0;

#'label'=value[UOM];[warn];[crit];[min];[max]

__DATA__
'label 1'=0.3345s;0.8s;1.2s;0.2s;3.2s
'label 2'=10%;7%;18%;2%;28%
'label 3'=0.5us;2.3us

输出

'label 1'=0.3345s;0.8s;1.2s;0.2s;3.2s
$VAR1 = {
          'crit' => '1.2s',
          'warn' => '0.8s',
          'value' => '0.3345s',
          'label' => '\'label 1\'',
          'max' => '3.2s',
          'min' => '0.2s'
        };

'label 2'=10%;7%;18%;2%;28%
$VAR1 = {
          'min' => '2%',
          'max' => '28%',
          'label' => '\'label 2\'',
          'value' => '10%',
          'warn' => '7%',
          'crit' => '18%'
        };

'label 3'=0.5us;2.3us
$VAR1 = {
          'min' => undef,
          'max' => undef,
          'label' => '\'label 3\'',
          'warn' => '2.3us',
          'value' => '0.5us',
          'crit' => undef
        };