Perl:这是正确的还是坏的正则表达式,以及如何改进它?

时间:2011-05-29 09:12:19

标签: regex perl

我正在尝试捕获传感器的温度输出,我有以下相关线:

temp1:       +39.5 C  (crit = +105.0 C)
Core 0:      +40.0 C  (high = +100.0 C, crit = +100.0 C)
Core 1:      +40.0 C  (high = +100.0 C, crit = +100.0 C)

我只需要每行的第一个温度(39.5,40.0,40.0)。问题当然是因为在“Core 0”/“Core 1”中有一个额外的空间,所以我不能真正使用单词编号。

我已经提出了以下正则表达式,但是我被告知使用.*对于正则表达式来说是一种有点懒惰和肮脏的方法。

$core_data =~ s/^.*\+(.*)C\ .*$/$1/g;

我想知道,有没有更严格或更好的方法来实现这一点,还是我做得好?

6 个答案:

答案 0 :(得分:6)

更简洁的正则表达式

/\+(\d+\.?\d*) C/

这将匹配第一个温度和可选的十进制值。

#!/usr/bin/perl
use strict;
use warnings;

my $re = qr{\+(\d+\.?\d*) C};
while (my $line = <DATA>){
    $line =~$re and print $1,"\n";
}
__DATA__
temp1:       +39.5 C  (crit = +105.0 C)
Core 0:      +40.0 C  (high = +100.0 C, crit = +100.0 C)
Core 1:      +40.0 C  (high = +100.0 C, crit = +100.0 C)

<强>输出:

39.5
40.0
40.0

答案 1 :(得分:3)

如果您只是想捕获第一个温度,我不明白为什么要进行搜索并替换正则表达式(s///g)。你的正则表达式似乎依赖.*贪婪。假设您可以依赖name: temp C (...格式,这个正则表达式将无需匹配整个字符串即可运行:

$core_data =~ m/^(?:\w*\b)*:\s*(\+?\d+\.\d+)/;

...或者在前面没有+的情况下捕获:

$core_data =~ m/^(?:\w*\b)*:\s*\+?(\d+\.\d+)/;

答案 2 :(得分:2)

更精确的正则表达式

 $core_data =~ s/^.*\+([\d.]+ )C\ .*$/$1/g;

但可能以下就足够了,因为只有数值看起来很有趣。

 $cpu_head = $1 if m/:\s*\+([\d.]+) C/;

注意:\ s代表任何空格,\ d代表任何数字。

答案 3 :(得分:2)

恕我直言,。*当它有意义时是完全正常的,尽管当你可以将它缩小到更具体的东西时,那就更好了。

在你的情况下,你可以说

S/^[^+]+\+([0-9.]) C.*$/$1/g

在这个正则表达式中,我专注于我正在寻找的东西,并将温度表征为一个数字序列,其中某个点在某处,而其余部分与我无关。由于每行中有两个温度,而你只需要第一个温度,我在开头使用[^ +],它匹配所有不是+的温度,所以它会在第一个温度开始的地方停止。一旦我达到温度,我就会把所有东西都用掉。*直到最后一行。

这只是一个推理的例子,它并不是假装你能解决问题的最佳正则表达式。

答案 4 :(得分:2)

这看起来更适合split而不是正则表达式。 split会自动清除所有不必要的空白,您无需提前计划数据的更改。

my $tag;
($tag, $core_data) = split (/:/, $core_data);
my @fields = split (/\s/, $core_data);
my $temp   = $fields[0];

这会将字符串"+39.5""+40.0"存储在不同的示例行中,我相信可以自动转换为数字。

此外,您可以轻松访问$tag中的行标签。

如果您愿意,可以使用正则表达式删除括号内的添加信息:

if ($core_data =~ s/\(([^\)]*)\)//) {
    my $tmp = $1;
    $tmp =~ s/[\s\+C]//g; # clear away junk
    %data = split (/=/, (split (/,/, $tmp)));
}
for my $key (keys %data) {
    printf "%-7s = %s\n", $key, $data{$key};
}

答案 5 :(得分:1)

我会编写一个解析输入并返回哈希值的通用函数。总的来说,我会使用这个正则表达式:

m/\A ([^:]+) : \s+ ([+-][0-9.]+) /xms

匹配一行。在1美元是匹配(即:“核心0”)和2美元的温度。我也会做一个从字符串到数字的转换,它会以这样的结尾:

my $temp_string = q{
temp1:       +39.5 C  (crit = +105.0 C)
Core 0:      +40.0 C  (high = +100.0 C, crit = +100.0 C)
Core 1:      +40.0 C  (high = +100.0 C, crit = +100.0 C)
Core 2:      -40.0 C  (high = +100.0 C, crit = +100.0 C)
};

my $temps = parse_temps($temp_string);

print "temp1:  ", $temps->{temp1}, "\n";
print "Core 0: ", $temps->{core0}, "\n";
print "Core 1: ", $temps->{core1}, "\n";
print "Core 2: ", $temps->{core2}, "\n";


sub parse_temps {
    my ( $str ) = @_;
    my %temp;
    for my $line ( split /\n/, $str ) {
        if ( $line =~ m/\A ([^:]+) : \s+ ([+-][0-9.]+) /xms ) {
            my $key   = $1;
            my $value = $2;

            $key   =~ s/\s+//g;
            $temp{ lc $key } = 0+$value;
        }
    }
    return wantarray ? %temp : \%temp;
}

该计划的输出:

temp1:  39.5
Core 0: 40
Core 1: 40
Core 2: -40