Perl:匹配两个文件之间的数组中的特定元素,并从文件中打印所需的元素

时间:2015-01-30 17:47:47

标签: perl

我想匹配两个文件之间的数组元素。第一个文件由六个元素组成,第二个文件由七个元素组成。

第一个文件中的前六个元素与第二个文件中的六个元素匹配。除了第二个文件中的最后一个元素外,两行相同。

如果该行与第一个文件匹配,我必须从第二个文件打印整行。

第一个文件 - errep.txt

 XYZ432345  Ethiopia    2567    89  ABC P   28-Apr-14
 XYZ432345  Poland      2567    98  ABC P   28-Apr-14

第二个文件 - rep.txt

XYZ432345   Ethiopia    2567    89  ABC P   28-Apr-14   No Issue
XYZ432345   Philippines 2467    92  ADC P   28-Apr-14   No Issue

期望输出

XYZ432345   Ethiopia    2567    89  ABC P   28-Apr-14   No Issue

以下是我正在尝试的代码

use strict;
use warnings;

open(OUT,"> Final Error Report.txt");

open(IN, "rep.txt");
my @values = <IN>;
close IN;
$flag=0;
$count=0;

open(IN, "errep.txt");
my @verify = <IN>;
close IN;

for my $ver (@verify) {
chomp $ver;
@elements= split("\t",$ver);    
 for my $rep (@values) {
 chomp $rep;
 @report = split("\t",$rep);


$newstring1="@elements[0 .. 5]";
$newstring2="@report[0 .. 5]";

if ($newstring1 =~/$newstring2/) {    
    ++$count;
    $flag=1;
}

if($flag==1){
$flag=0;
print OUT "$rep\n";
            }
    }
}
print OUT "Total Count:$count";

如果任何人可以改进这个代码非常感谢。感谢您的建议..

3 个答案:

答案 0 :(得分:0)

  • Perl(和大多数语言)中的逻辑AND &&不是&
  • 如果要测试字符串相等性,请使用例如$code eq $code1(不是$code =~ $code1,进行正则表达式匹配)
  • 使用嵌套循环是一种缓慢的处理方式,但如果一个或两个输入文件相当小,它可能足够快。但要使其工作,您需要在开始之前将REPORT中的所有行加载到数组中。目前,在外循环的第二次迭代中,<REPORT>将立即返回undef,因为您已经从此文件中读取了所有内容。 (或者,您可以使用seek()来回放文件指针。)
  • 您可以使用哈希表,也可以对两个输入进行排序,而不是嵌套循环,但只需花费太长时间就可以执行此操作。

答案 1 :(得分:0)

你是什么意思匹配。这两条线并不完全匹配。你是说第一个文件中的前六个元素是否与第二个文件中的六个元素匹配?线条是否相同,除了第七个文件中的最后一个元素?

每当你说“我必须找到一个匹配”时,你首先考虑使用哈希值。如果您的文件非常大,则需要考虑使用外部数据库。

我还将使用更多现代 Perl。其中包括use strictuse warnings。这意味着使用my声明变量。这可以捕获您遇到的许多编程错误。

我创建了一个名为%file_hash的哈希。此哈希的关键是文件,其中的字段用冒号而不是空格分隔。这样,我不必担心两个文件之间的空间差异,因为字段的顺序是相同的。

此哈希的数据将是行本身。例如:

$file_hash{XYZ432345:Ethiopia:2567:89:ABC:P:28-Apr-14} =
   "XYZ432345   Ethiopia    2567    89  ABC P   28-Apr-14   No Issue"

现在,如果我对第二个文件执行相同的操作,我可以看到%file_hash中是否存在该密钥。如果确实如此,我知道我之前在文件#1中看过那条线,我将打印出这些数据。

通过使用哈希,我只需要遍历每个文件一次,而不是将每一行相互匹配。如果文件#1包含100行,而文件#2包含200行,那么我将经历300次迭代。按照你的方式,你循环100 * 200次,或2000次迭代。

use strict;
use warnings;
use autodie;  # Will kill my program if file doens't open
use feature qw(say);

use constant {
    file_1  => 'rep.txt',
    file_2  => 'errep.txt',
};

#
# Open file #1 and build the hash index
#
open my $fh_1, '<', file_1;

my %file_hash;
while ( my $line = <$fh_1> ) {
    chomp $line;
    my $key = substr( $line, 0, 53);
    $key =~ s/\s+/:/g;
    $file_hash{$key} = $line;
}
close $fh_1;

#
# Open file #2 and compare the lines against that hash index
#
open my $fh_2, '<', file_2;
while ( my $line = <$fh_2> ) {
    chomp $line;
    my $key = $line;
    $key =~ s/\s+/:/g;
    if ( $file_hash{$key} ) {
        say "$file_hash{$key};"
    }
}

答案 2 :(得分:0)

我建议你使用这样的东西。请注意以下内容

  • use strictuse warnings,以及my所有变量的声明

  • 有意义的变量名称

  • 词法文件句柄而不是全局句柄(my $rep_fh而不是INFILE)等。

  • 我已将rep.txt的内容复制到数组@rep中,以便多次保存重新打开文件

  • 我已将字段留在数组中,并使用List::Util中的all函数检查前六个字段是否匹配。如果您不单独使用它们,则无需将值复制到自己的标量变量中。

#!/usr/bin/perl

use strict;
use warnings;

use List::Util 'all';

open my $rep_fh,  '<', 'rep.txt' or die qq{Unable to open "rep.txt" for input: $!};
my @rep = <$rep_fh>;
chomp @rep;
close $rep_fh;

open my $errep_fh, '<', 'errep.txt' or die qq{Unable to open "errep.txt" for input: $!};

while (my $errep_record = <$errep_fh>) {

  chomp $errep_record;
  my @errep_record = split /\t/, $errep_record;

  for my $rep_record (@rep) {

    my @rep_record = split /\t/, $rep_record;

    if ( all { $rep_record[$_] eq $errep_record[$_] } 0 .. 5 ) {
      print "$rep_record\n";
    }
  }
}