重复匹配组时,一些捕获组似乎丢失
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
};
试图将 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
};