2011-05-29 35 views
6

我试图捕捉传感器的温度输出,为此,我有以下相关线路: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; 

我想知道,是否有更紧密或更好的方法来完成这个或我做得好吗?

+0

你说的是好是坏呢?效率似乎并不重要,除非你必须匹配成千上万的模式。似乎并非如此。 – VGE 2011-05-29 09:17:44

+0

OTOH,可读性**是**重要的。确保你有附近的评论。 – pavium 2011-05-29 09:19:56

+0

以学习为名的效率:)。另外我注意到我的正则表达式产生了一个带有空间主角特征的结果。我不认为这对我的目的很重要,但我仍然想知道如何摆脱它 – DanH 2011-05-29 09:20:39

回答

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 
2

一个更精确的正则表达式

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

不过,也许以下就足够了,因为只有numeical价值似乎很有趣。

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

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

2

恕我直言,。*是非常好的,当它是有道理的,虽然当你可以缩小到更具体的东西,然后一切都好。

在你的情况,你可以说

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

在这个表达式,我专注于我正在寻找和表征温度的一个点某处数字序列,而剩下的只是不相关对我来说。由于每行有两个温度,而您只需要第一个温度,所以我在开始时使用了[^ +],这与所有不是+的事物都匹配,所以它会在第一次温度开始时停止。一旦我得到了温度,我就把所有东西都吞下去。*直到行尾。

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

3

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

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

...或者在前面没有+捕捉:

$core_data =~ m/^(?:\w*\b)*:\s*\+?(\d+\.\d+)/; 
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}; 
} 
+0

不幸的是,由于'Core 0'和'Core 1'中的空间,而不是'temp1'中的空间,所以拆分空间对我来说不起作用。 – DanH 2011-05-29 10:07:07

+0

@DanH现在修复..不能检查'$ {1}'是否可以工作(因为我没有在这台计算机上安装perl)。我假设'_'会尝试将其自身附加到标量上。 – TLP 2011-05-29 10:08:44

1

我会写一个分析的输入,并返回一个散列一般功能。一般我会用这个正则表达式:

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

这符合一条线。在$ 1中是相匹配的(即:“Core 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