Perl正则表达式多行匹配成哈希

时间:2012-03-29 11:17:28

标签: regex perl hash

我正在成功解析cisco配置文件,并使用多行正则表达式抓取每个标记之间的配置部分(cisco使用!符号):

/(search string)/i .. /^!/ 

我的代码如下:

#!/usr/bin/perl -w
use strict;
use Data::Dumper;

my (@results, @data) ;

#Test data to simulate a while loop on a file-handle running through a config file.
@data =  (
    "vlan 81" ,
    " name Vlan 81 test1" ,
    "!" ,
    "vlan 82" ,
    " name Vlan 82 test2" ,
    "!" ,
    "vlan 83" ,
    " name Vlan 83 test3" ,
    "!"
);

foreach ( @data ) {
    if ( /vlan/i .. /^!/ ) {
         push  (@results , $_) ;                
    }
}

print Dumper ( @results ) . "\n" ;

exit;

它的效果非常好,但是我想将结果推送到哈希中,每段代码都是一个匿名数组,所以结果看起来像:

%Vlan -> [Vlan 81, name Vlan 81 test1] , [Vlan 82, name Vlan 82 test2] , [Vlan 83, name Vlan 83 test3]

但是我无法弄清楚如何做到这一点,我的代码在搜索字符串和标记之间的每一行匹配,我只是逐行将结果重建为另一个数组。

非常感谢任何帮助。

干杯,

安迪

3 个答案:

答案 0 :(得分:4)

我不确定你对哈希的意思,因为你描述的内容只是匿名数组的列表。没有键,所以你只能生成一个数组。如果您可以解释数据的哪一部分是关键,那么我们可以去哈希。

use warnings pragma优于-w shebang修饰符,因为它更灵活,可以否定。

范围运算符..可能很可爱,但您不能在任何可能的情况下将其用于使用。

将输入分隔符设置为"!\n"将允许您一次读入所有相关行,然后可以将其推送到阵列。

代码看起来像这样

use strict;
use warnings;

use Data::Dumper;

my @Vlan;

$/ = "!\n";

while  (<DATA>) {
  chomp;
  push @Vlan, [split /[\r\n]+/];
}

print Data::Dumper->Dump([\@Vlan], ['*Vlan']);

__DATA__
vlan 81
name Vlan 81 test1
!
vlan 82
name Vlan 82 test2
!
vlan 83
name Vlan 83 test3
!

<强>输出

@Vlan = (
          [
            'vlan 81',
            'name Vlan 81 test1'
          ],
          [
            'vlan 82',
            'name Vlan 82 test2'
          ],
          [
            'vlan 83',
            'name Vlan 83 test3'
          ]
        );

修改

如果哈希的键始终是记录集的第一行,则此程序会根据您的请求生成哈希

use strict;
use warnings;

use Data::Dumper;

my %Vlan;

$/ = "!\n";

while  (<DATA>) {
  chomp;
  my ($k, $v) = split /[\r\n]+/;
  $Vlan{$k} = $v;
}

print Data::Dumper->Dump([\%Vlan], ['*Vlan']);

__DATA__
vlan 81
name Vlan 81 test1
!
vlan 82
name Vlan 82 test2
!
vlan 83
name Vlan 83 test3
!

<强>输出

%Vlan = (
          'vlan 81' => 'name Vlan 81 test1',
          'vlan 83' => 'name Vlan 83 test3',
          'vlan 82' => 'name Vlan 82 test2'
        );

答案 1 :(得分:3)

将程序结束更改为

my %Vlan;

for (@data) {
  if (my $inside = /vlan/i .. /^!/) {
    if ($inside =~ /E0$/) {
      s/^\s+//, s/\s+$// for @results;  # trim whitespace
      $Vlan{ $results[0] } = join ", ", @results;
      @results = ();
    }
    else {
      push @results, $_;
    }
  }
}

print Dumper \%Vlan;

当右侧条件为真时,.. range operator会返回以"E0"结尾的值,因此我们可以将其用作何时将新条目放入%Vlan的提示

  

返回的值是false的空字符串,或者是true的序列号(以1开头)。为遇到的每个范围重置序列号。一个范围内的最终序列号附加了字符串"E0",它不会影响其数值,但如果要排除端点,则可以搜索一些内容。

您的最终目标尚不清楚,但似乎您希望哈希值是字符串而不是数组。 Perl的join通过在值列表中插入元素之间的某些分隔符来创建字符串。上面的代码会删除@results中每个值的前导和尾随空格,然后再使用它们填充%Vlan

输出:

$VAR1 = {
          'vlan 81' => 'vlan 81, name Vlan 81 test1',
          'vlan 83' => 'vlan 83, name Vlan 83 test3',
          'vlan 82' => 'vlan 82, name Vlan 82 test2'
        };

答案 2 :(得分:2)

这个保持状态而不是做多行:

my %Vlan;

#Test data to simulate a while loop on a file-handle running through a config file.
@data =  (
    "vlan 81" ,
    " name Vlan 81 test1" ,
    "!" ,
    "vlan 82" ,
    " name Vlan 82 test2" ,
    "!" ,
    "vlan 83" ,
    " name Vlan 83 test3" ,
    "!"
);

foreach ( @data ) {
    if (/ name (\w+ \d+) /) {
      my $name = lc $1;
      die("undef $name") if (not defined $Vlan{$name});
      $Vlan{$name} = [$name, $_];
    } elsif ( /^(\w+ \d+)$/ ) {
      my $name = lc $1;
      $Vlan{$name}++;
    }
}

print Dumper ( %Vlan ) . "\n" ;

exit;