将日志文件中的数据提取到Perl哈希

时间:2015-06-10 07:06:33

标签: regex perl hash

我有四个日志文件。每个日志文件都采用相同的格式,因此我们将重点放在一个日志文件上。

在每个日志文件中,我必须提取诸如Computer Name之类的信息。我不会在这里发布日志文件,因为它有超过46,000行。

我的主要目标是,一旦提取了这些信息,我就会将其存储在哈希中。这些哈希稍后将用于插入语句到数据库表中。

到目前为止,我所做的是

use strict;
use warnings;

my $filename = 'IGXLEventLog.3.17.2015.20.25.12.625.log';
open(my $fn, '<', $filename) or die "Could not open file '$filename': $!";

our %details;

while ( my $row = <$fn> ) {
    chomp $row;

    if ( $row =~ /Computer Name:\s*(\S+)/i ) {
        print $1, "\n";
    }

    if ( $row =~/Operating System:\s*(.*)/i ) {
        print $1, "\n";
    }

    if ( $row =~/IG-XL Version:\s(.*?)\;/ ) {
        print $1, "\n";  
    }
}

问题1

从日志文件中提取计算机名称和操作系统没有问题。但是,日志文件中的IG-XL Version出现两次。因此,我从打印$1获得的是:

8.00.01_uflx (P7)
8.00.01_uflx (P7)

原始记录是

Current IG-XL Version: 8.00.01_uflx (P7); Build: 11.10.12.01.31
Current IG-XL Version: 8.00.01_uflx (P7); Build: 11.10.12.01.31

因此,您可以看到我设法隔离了我想要的数据,但我得到了两个结果。我现在的主要目标是解决如何只获得第一场比赛。

对此有何帮助?有什么我做错了吗?请告诉我。

问题2

这个问题不是我的重点,但是在我解决第一个问题之后。在日志文件中,有一个数据结构如下的部分:

2.0  HSD-U       664-999-01 c301036 1251-A 5445
      ChanBoard0    233-455-00 c303bb6 1521-A 5445
      ChanBoard1    321-493-00 c303496 1321-A 5445
6.0   DC-07       888-375-02 0C31F8F1 1330-A 5445
    aka: 604-375-00
      DC-07       123-456-01 0C6203EF 1150-A 5445
    aka: 939-420-00
7.0   DC-07       613-493-00 c303496 1321-A 5445
    aka: 466-456-65
      DC-07       613-493-00 c303496 1321-A 5545

请注意,左侧有数字(不被视为小数),例如2.06.07.0等。 (例如,其他一些数字将是1.45.6057.58。)

在字符串的这一部分中,我想根据数字只获得第一行。因此对于2.0我只会选择行2.0 HSD-U 664-999-01 c301036 1251-A 5445并忽略没有数字的行。

在这一行中,我想分别获取字段2.0HSD-U,并将这两个字段分别分配给一个单独的哈希。

所以我需要提取总共四种不同的数据,最后一种类型是最多的数据。

编辑:根据鲍罗丁的回答我做了什么

while(<$fn>)
{
    if ( /Computer Name:\s*(\S+)/i ) {
        $details{comp_name} //= $1;
        print $details{comp_name}, "\n";
    }
    elsif ( /Operating System:\s*(.*)/i ) {
        $details{op_sys} //= $1;
        print $details{op_sys}, "\n";
    }
    elsif ( /IG-XL Version:\s*([^;]*)/i ) {
        $details{igxl_vn} //= $1;
        print $details{igxl_vn}, "\n";
    }
    elsif ( /^([\d.]+)\s+(\S+)/ ) {
        $details{slot} //= $1;
        $details{name} //=$2;
        print $details{slot}, "\n", $details{name};
    }
}

我的输出:

UFLEX-06
Windows XP Service Pack 3
8.00.01_uflx (P7)
8.00.01_uflx (P7)    <-Duplicated
HSD-U2.0             <-Duplicated
HSD-U2.0             <-Duplicated
HSD-U2.0             <-Duplicated
HSD-U2.0             <-Duplicated
HSD-U2.0             <-Duplicated
HSD-U2.0             <-Duplicated
HSD-U8.00.01_uflx (P7) <-Weird line

这个预期的输出是我想要的,直到第3行。

预期的一个是:

UFLEX-06
Windows XP Service Pack 3
8.00.01_uflx (P7)
2.0
HSD-U
5.0
Gigabit
6.0
MattersNot
7.9
MatterMatter
15.20
Knnccb

编辑2:@Borodin两者都是字符串。实际上都是字符串。我要做的是,对于所有这些值,我将它们插入到我的数据库中的表中,在那里我将创建一个只包含文本的INSERT INTO TABLE1(cp_name, os, version, slot, slot_name) values('UFLEX-06', 'Windows XP dot dot', '8.0.0_uflx', '2.0', 'HSD-U')的SQL文件。这只是为了更好地理解。

slots中会有很多cp_name。把它当作一台电脑吧。将有4个ram插槽。每张ram卡都有不同的名称。这个名称或者更确切地说是一种识别哪个卡位于哪个位置的方式,所以公羊名称是EatYou。那个ram卡在插槽3中。所以我必须在数据库中插入除了ram名称和ram插槽号以外的所有细节,这些都是不同的。

回到主要观点,这就是为什么我试图通过将每个值分配给一个哈希数组来找到一个简单的方法来做到这一点,这样当我创建sql文件时,它很容易我要分配插入值。

2 个答案:

答案 0 :(得分:2)

我会这样写。对文件记录使用显式变量会产生更多噪音,因此我使用了Perl的默认$_

表达式$details{comp_name} //= $1等仅在哈希元素尚未具有值

时才分配哈希元素

你没有说清楚你想如何在散列中存储点分小数,所以我使用第一个字段作为键,第二个字段作为散列

use strict;
use warnings;

my $filename = 'IGXLEventLog.3.17.2015.20.25.12.625.log';
open my $fh, '<', $filename or die "Could not open file '$filename': $!";

my %details;

while ( <$fh> ) {

    if ( /Computer Name:\s*(.*\S)/i ) {
        $details{comp_name} //= $1;
    }
    elsif (/Operating System:\s*(.*\S)/i ) {
        $details{op_sys} //= $1;
    }
    elsif (/IG-XL Version:\s*([^;]*)/ ) {
        $details{igxl_vn} //= $1;
    }
    elsif ( /^([\d.]+)\s+(\S+)/ ) {
        $details{$1} //= $2;
    }
}

use Data::Dump;
dd \%details;

<强>输出

{
  "2.0"       => "HSD-U",
  "6.0"       => "DC-07",
  "7.0"       => "DC-07",
  "comp_name" => "UFLEX-06",
  "igxl_vn"   => "8.00.01_uflx (P7)",
  "op_sys"    => "Windows XP Service Pack 3",
}

答案 1 :(得分:0)

第一个问题已在那里得到解答:Stopping regex at the first match, it shows two times

至于第二个,你需要使用起始锚点(^),然后紧跟一些数字和点:

if($row =~/^[\d\.]+\s+(\S+)/)
{
    print $1, "\n";  
}

以下是这个正则表达式的工作原理:https://regex101.com/r/vY7aL6/1