根据多个列出的标准跳过行

时间:2017-10-03 08:43:40

标签: perl

我在文件<$IN>上进行迭代,逐行处理。我希望根据分割字段中保存的值跳过一些行,我目前通过以下方式完成:

while (<$IN>) {
    chomp $_;
    my(@F) = split("\t", $_);
    next if ($F[0] == 1 && $F[1] > 20 && $F[1] < 40);

但是我有多个这样的行和标准要检查。我更愿意加载包含测试值的.txt文件,例如:

1   20  40
1   63  68
2   1   10
2   12  18
3   3   9

找到一种较短的方法来测试任何给定的线对这些范围,而不需要像我现在那样需要很多线:

next if ($F[0] == 1 && $F[1] > 20 && $F[1] < 40);
next if ($F[0] == 1 && $F[1] > 63 && $F[1] < 68);
next if ($F[0] == 2 && $F[1] > 1 && $F[1] < 10);
next if ($F[0] == 2 && $F[1] > 12 && $F[1] < 18);
next if ($F[0] == 3 && $F[1] > 3 && $F[1] < 9);

有人能提供一个如何以更简洁的方式做到这一点的例子吗?

2 个答案:

答案 0 :(得分:3)

将范围存储在数据结构中,例如

#!/usr/bin/perl
use warnings;
use strict;

my %skip = ( 1 => [ [20, 40], [63, 68] ],
             2 => [ [ 1, 10], [12, 18] ],
             3 => [ [ 3,  9] ]
);

while (<>) {
    my @F = split;
    next if grep $F[1] >= $_->[0] && $F[1] <= $_->[1],
        @{ $skip{ $F[0] } };
    print;
}

答案 1 :(得分:3)

您可以毫无问题地将该配置外包到文件中。我会使用散列(ref)作为列号,使用数组ref作为最小值和最大值,如下所示。由于每个字段可以有多对值,因此需要一个数组数组。

use strict;
use warnings;
use Data::Printer;

# open my $fh, '<', 'config.txt' or die $!;
my $config;
while (my $line = <DATA>) {
    chomp $line;
    my ( $col, $min, $max )= split /\s+/, $line;
    push @{ $config->{$col} }, [ $min, $max ];
}

p $config;
__DATA__
1   20  40
1   63  68
2   1   10
2   12  18
3   3   9

数据结构如下所示:

\ {
    1   [
        [0] [
            [0] 20,
            [1] 40
        ],
        [1] [
            [0] 63,
            [1] 68
        ]
    ],
    2   [
        [0] [
            [0] 1,
            [1] 10
        ],
        [1] [
            [0] 12,
            [1] 18
        ]
    ],
    3   [
        [0] [
            [0] 3,
            [1] 9
        ]
    ]
}

哈希使得exists可以很容易地检查字段哈希值是否完整。然后你可以像这样使用它。

use strict;
use warnings;

# see the code example above
my $config = {
    1 => [ [ 20, 40 ], [ 63, 68 ] ],
    2 => [ [ 1,  10 ], [ 12, 18 ] ],
    3 => [ [ 3,  9 ] ],
};

sub skip_line {
    my @F = @_;

    return unless exists $config->{$F[0]}; # nothing to skip in this field

    foreach my $pair (@{ $config->{$F[0]} }) {
        return 1 if $F[1] > $pair->[0] && $F[1] < $pair->[1];
    }
}

while (<DATA>) {
    chomp;
    my @F = split /\s+/; # \t

    next if skip_line(@F);

    print; # output unskipped lines
}

__DATA__
1 21 30
2 19 20

您当然可以使用较短的表单而不是skip_line函数,如choroba's answer所示。