Perl正则表达式多行匹配转换为哈希表

4

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

/(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]

但我不知道该怎么做,我的代码在搜索字符串和标记之间每行匹配,最终只是逐行重建结果到另一个数组中。

非常感谢任何帮助。

谢谢,

安迪


你的目标不是很清晰,因为你的示例只有一个默认键Vlan的单个条目。对于这个键,你有一个数组列表。难道你的意思不是对于每个键(例如Vlan 81),你都有一个值的哈希表吗? - Matteo
是的,你说得对,感谢你的评论,我会尽量在未来表达更清晰。 - user1039417
3个回答

5

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

use warnings 原语比 -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'
        );

这正是我想要实现的,我会尝试一下。我也没有意识到有这么多特殊变量存在。感谢您的帮助。 - user1039417

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;
.. 范围操作符返回一个以 "E0" 结尾的值,当右侧条件为真时,因此我们可以将其用作在何时向 %Vlan 中添加新条目的提示。

如果为 false,则返回空字符串;如果为 true,则返回序列号(从 1 开始)。每遇到一个范围,序列号就会重置。范围中的最后一个序列号会在其后附加字符串 "E0",这不会影响其数值,但如果您想要排除终点,则可用此搜索。

你的最终目标并不清楚,但似乎你希望哈希值是字符串而不是数组。Perl 的 join 可通过在一组值之间插入某个分隔符来创建一个字符串。上面的代码在使用这些值填充 %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'
        };

关于 E0 的特性很有趣,我已经忘记了这个行为。我也会测试你的代码。 - user1039417

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;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接