将替换限制为Perl中每行的前n个实例

时间:2015-11-11 20:13:10

标签: regex perl hash replace

我有类似Stack Overflow post Substituting array elements from one tab delimited file with hash values from another file using Perl 的情况。我试图用特定列中各自的值替换匹配哈希键的字符串。

    Given the @array:

    a b abbd
    cc d abcd
    gg hh cdag

and the %hash:

$VAR1 = {
    'a'  => 'GAT_1',
    'b'  => 'GAT_2',
    'cc' => 'GAT_3',
    'd'  => 'GAT_4',
    'gg' => 'GAT_5',
    'hh' => 'GAT_6',
};

我已经尝试过这段代码,但它不起作用。如何限制仅替换匹配键的前两个实例(列)? (也就是说,保持第三列不变?)

foreach $line (@array) {
    my @cols = split (/\s+/, $line);
    $cols[0] = $hash{cols[0]};
    $cols[1] = $hash{cols[1]};
    push @newarray, $line;
}


Expected output:
GAT_1 GAT_2 abbd
GAT_3 GAT_4 abcd
GAT_5 GAT_6 cdag

3 个答案:

答案 0 :(得分:1)

只需在替换后加入列:

my $line_after_lookup;
foreach $line (@array) {
    my @cols = split (/\s+/, $line);

    if (defined($$VAR1{$cols[0]})) { $cols[0] = $$VAR1{$cols[0]}; }    
    if (defined($$VAR1{$cols[1]})) { $cols[1] = $$VAR1{$cols[1]}; } 

    #
    # When using a hash instead of a hash reference, replace the previous 2 statements with the following 2 lines:
    # if (defined($hash{$cols[0]})) { $cols[0] = $hash{$cols[0]}; }
    # if (defined($hash{$cols[1]})) { $cols[1] = $hash{$cols[1]}; }
    #

    $line_after_lookup = join ( ' ', @cols );
    push @newarray, $line_after_lookup ;
}

答案 1 :(得分:0)

这就是诀窍:

#!/usr/bin/env perl
use strict;
use warnings;
use Data::Dumper; 

my %substitute = (
    'a'  => 'GAT_1',
    'b'  => 'GAT_2',
    'cc' => 'GAT_3',
    'd'  => 'GAT_4',
    'gg' => 'GAT_5',
    'hh' => 'GAT_6',
);

my @newarray;

while (<DATA>) {
    my @fields = split;
    $fields[0] = $substitute{ $fields[0] };
    $fields[1] = $substitute{ $fields[1] };
    push ( @newarray, join( " ", @fields ));
}

print Dumper \@newarray;

__DATA__
    a b abbd
    cc d abcd
    gg hh cdag

打印:

$VAR1 = [
          'GAT_1 GAT_2 abbd',
          'GAT_3 GAT_4 abcd',
          'GAT_5 GAT_6 cdag'
        ];

您的工作无效,因为您正在更改@fields而不是$line的内容。然而另一个可能的问题是你的分裂 - split /\s+/处理领先的空格不同(你得到一个空字段)。

答案 2 :(得分:0)

如何限制只替换匹配键的前两个实例(列)? (即保持第三列不变?)

一种方法是使用正则表达式,只选择那两列并执行适当的替换。

插图(已编辑):

#!/usr/bin/perl

my @array = (
    "a b abbd",
    "cc d abcd",
    "gg hh cdag",
    "ii jj kmln"  # to show what happens when no mapping exists 
);

my %hash = (
'a' => 'GAT_1',
'b' => 'GAT_2',
'cc' => 'GAT_3',
'd' => 'GAT_4',
'gg' => 'GAT_5',
'hh' => 'GAT_6',
);

sub replace { $hash{$_[0]} || $_[0]; } # original string if no mapped value

sub convert { replace($_[0]) . $_[1] . replace($_[2]); }

my @newarray = map
{
    my $line = $_;
    $line =~ s/(\w+)(\W+)(\w+)/convert($1, $2, $3)/e;
    $line;
} @array;

print "$_\n" for @newarray;

此处需要替换:

    $line =~ s/(\w+)(\W+)(\w+)/convert($1, $2, $3)/e;

e表达式上的s///修饰符会导致第二部分中的替换表达式被计算,从而调用sub convert()。 convert()的参数来自第一部分正则表达式中的捕获:(\w+)捕获第一个单词字符序列,(\W+)捕获非单词字符,如空格或标点符号,再次(\w+)捕获第二个单词字符序列。线路的其余部分无关紧要,根本不需要解析,保持不变。

如果映射值不可用,您可能还需要考虑会发生什么。在这种情况下,上面的代码保留原始字符串。