从grep到perl:perl的逆匹配

时间:2019-02-11 12:14:15

标签: perl grep

我很长时间不使用perl编程,需要一些推动。

我需要比较两个列表,以便仅保留不匹配的行。

我的第一个文件如下:

1   pf1 er2 0,4  
2   pf1 er3 0,56  
3   pf1 er6 0,72365  
4   er3 pf3 0,263
5   pf5 er2 0,28473

第二个文件是:

pf1 er2
pf1 er3
er2 pf1
er3 pf1

我想要一个输出,如:

3   pf1 er6 0,72365  
4   er3 pf3 0,263
5   pf5 er2 0,28473

我以前是通过grep -Fvf second_file.txt first-file.txt > output.txt

完成的

现在我需要在perl中执行相同的操作,但是我无法组织代码。

open(HAN, "< $file_1") ||  die "Impossibile aprire il file $file_1";
@r = <HAN>;
close(HAN);
open(RES, "< $file_2") ||  die "Impossibile aprire il file $file_2";
@c = <RES>;
close(RES);

for ($i=0; $i<=$#r; $i++){
    ($num, $id1, $id2, $v) = split (/\t/, $r[$i], 4);

    $ppi1 = $id1."\t".$id2;

    for($t=0; $t<=$#c; $t++){
        ($iid1, $iid2) = split (/ /, $c[$t]);
        $orto1 = $iid1."\t".$iid2;
        $orto2 = $iid2."\t".$iid1;

        if( ($ppi1 ne $orto1) || ($ppi1 ne $orto2) ){
            print "$ppi1\n";
        }
    }
}

任何建议都非常欢迎!

3 个答案:

答案 0 :(得分:2)

基于该样本数据,如果第二列和第三列与第二个文件的一行的第一列和第二列匹配,则要从第一文件中排除行。将第二个文件的列存储在哈希中,然后在读取第一个文件时检查这些键的存在是一种简单,非常省时的方法:

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

my ($data_file, $excludes_file) = @ARGV;

my %excludes;
open my $ex, "<", $excludes_file;
while (<$ex>) {
  chomp;
  my @F = split;
  $excludes{$F[0]}->{$F[1]} = 1;
}

open my $data, "<", $data_file;
while (<$data>) {
  my @F = split;
  print unless exists $excludes{$F[1]}->{$F[2]};
}

运行它将给出:

$ perl filter.pl file1.txt file2.txt
3   pf1 er6 0,72365  
4   er3 pf3 0,263
5   pf5 er2 0,28473

答案 1 :(得分:1)

这不是一个非常详尽但可行的解决方案:

    #!/usr/bin/env perl 

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

#my @first_file_lines = split "\n", `cat ./first_file.txt`;
#my @second_file_lines = split "\n",`cat ./second_file.txt`;
open( my $fh, '<', './first_file.txt' );
open( my $fh1, '<', './second_file.txt' );
chomp ( my @first_file_lines = <$fh> );
chomp (my @second_file_lines = <$fh1>) ;

close( $fh );
close( $fh1 );


my @output = grep { filter( $_, \@second_file_lines ) } @first_file_lines;

sub filter {
    my $current    = shift;
    my $compare_to = shift;

    for my $comp ( @$compare_to ) {
        my $comp1 = $comp;
        $comp1 =~ s/\|/ /;
        if ( $current =~ /^$comp1/ ) {
            say 'equal: ' . "$current   :  $comp";
            return;
        }
    }


    return $current;
}

say Dumper( @first_file_lines );
say Dumper( @second_file_lines );

for my $out ( @output ) {
    `echo "$out" >> ./output.txt`;
}

答案 2 :(得分:1)

这应该有效:

  • 从参考文件中读取行
    • 引用该行的内容以使其兼容于从中编译正则表达式
  • 编译一个将所有匹配项或在一起的正则表达式
    • 在您的示例中为(?:pf1 er2|pf1 er3|er2 pf1|er3 pf1)
  • 从STDIN中读取行
    • 除非正则表达式匹配,否则将行打印到STDOUT
#!/usr/bin/perl
use strict;
use warnings;

my($reference) = @ARGV;

my $fh;
open($fh, "<". $reference)
    or die "open '${reference}': $!\n";
my @matches;
while (<$fh>) {
    chomp;
    push(@matches, quotemeta);
}
close($fh)
    or die "close '${reference}': $!\n";

# compile combined regex
my $regex = join('|', @matches);
$regex = qr/(?:${regex})/;

while (<STDIN>) {
    print unless $_ =~ $regex;
}

exit 0;

测试输出:

$ cat dummy1.txt
1   pf1 er2 0,4  
2   pf1 er3 0,56  
3   pf1 er6 0,72365  
4   er3 pf3 0,263
5   pf5 er2 0,28473

$ cat dummy2.txt
pf1 er2
pf1 er3
er2 pf1
er3 pf1

$ perl dummy.pl <dummy1.txt dummy2.txt
3   pf1 er6 0,72365  
4   er3 pf3 0,263
5   pf5 er2 0,28473