在匹配之间提取数组元素

时间:2014-06-04 12:53:37

标签: arrays regex perl

我有一个字符串数组,我想提取数组的元素 在包含关键字'start'的字符串之间,直到包含字符';'的字符串发生。 这些关键字之间的元素应该由一个空格拆分并写入散列。

示例:对于数组(" kk", "aa1", " 1 asa ", " start", "a 1", "b 2", "c 3", ";", "aaa"),我想提取键/值对a 1b 2c 3并将它们写入散列。

我的实现给出了正确的结果,但相当幼稚,涉及循环和嵌套的if语句:

my @lines = ( "z 10", "  start", "a  1", "b  2", "c  3", ";", "aaa" );

my %map;
my $start;
foreach my $line (@lines) {
    chomp $line;
    if ( $line =~ m/start/ ) {
        $start = 1;
    }
    else {
        if ($start) {
            if ( $line =~ m/;/ ) {
                last;
            }
            my @arr = split " ", $line;
            $map{ $arr[0] } = $arr[1];
        }
    }
}

foreach my $k ( keys %map ) {
    my $val = $map{$k};
    print "Key : $k val : $val \n";
}

有更优雅的方法吗?

3 个答案:

答案 0 :(得分:2)

使用单个map调用可以做到最完整:

my @subset = grep { /start/../;/ } @lines;
my %map = map split, @subset[1 .. $#subset-1];

for my $k (keys %map) {
  my $val = $map{$k};
  print "Key : $k val : $val \n";
}

<强>输出

Key : a val : 1 
Key : c val : 3 
Key : b val : 2 

答案 1 :(得分:1)

这个问题是触发器操作员的理想选择......

use strict;
use warnings;
use Data::Dumper;

my @lines = ("  kk", "aa1", "   1 asa ", "  start", "a  1", "b  2", "c  3", ";", "aaa");

my @sub;
foreach (@lines) {
    push(@sub, split) if (/  start/ .. /\;/);  # flip flop
}    
shift @sub;  # Remove the start flag.
pop @sub;    # Remove the end flag.

my %hash = @sub;  # Convert to hash.
print Dumper(\%hash);

输出:

$VAR1 = {
      'c' => '3',
      'a' => '1',
      'b' => '2'
    };

触发器是/ start / .. / end / part。

正如评论中所指出的,这可以进一步重构为:

my @sub = map +split, grep { / start/ .. /;/ } @lines; 
shift @sub;
pop @sub;    
my %hash = @sub;

或者:

my @sub = map { / start/ .. /;/ ? split : () } @lines;
shift @sub;
pop @sub;    
my %hash = @sub;

这些实现了相同的结果,但我已经离开了我的第一个解决方案,因为我认为这有助于说明触发器操作员正在做什么。

答案 2 :(得分:1)

考虑到它的可读性,我实际上最喜欢Borodin的解决方案。

但是,您可以对范围返回值进行测试以限制结果:

use strict;
use warnings;

my @lines = ( "z 10", "  start", "a  1", "b  2", "c  3", ";", "aaa" );

my %hash = map {split} grep {
    my $r = /start/ .. /;/;
    $r && $r !~ /^1$|E/
} @lines;

use Data::Dump;
dd \%hash;

输出:

{ a => 1, b => 2, c => 3 }