使用 perl 循环遍历值列表 (x) 并与具有值范围的另一个文件进行比较

Using perl to cycle through a list of values (x) and compare to another file with value ranges

我有两个文件。一个文件有一个像这样的值列表

NC_SNPStest.txt

250 
275 
375

另一个文件有 space 分隔信息。第一列是范围的第一个值,第二列是范围的第二个值,第五列是范围的名称,第八列是作用于该范围的内容。

promoterstest.txt

20 100 yaaX F yaaX 5147 5.34 Sigma70 99
200 300 yaaA R yaaAp1 6482 6.54 Sigma70 35
350 400 yaaA R yaaAp2 6498 2.86 Sigma70 51

我正在尝试编写一个脚本,该脚本从文件 1 中获取第一行,然后逐行解析文件 2 以查看该值是否落在前两列之间的范围内。

找到第一个匹配项后,我想打印文件 1 中的值,然后打印文件 2 中匹配项所在行的第 5 列和第 8 列的值。如果在文件 2 中未找到匹配项,则只需打印文件 1 中的值并继续。

看起来这应该是一项足够简单的任务,但我在循环浏览这两个文件时遇到了问题。

这是我写的:

#!/usr/bin/perl

use warnings;
use strict;

open my $PromoterFile, '<', 'promoterstest.txt' or die $!;
open my $SNPSFile, '<', 'NC_SNPtest.txt' or die $!; 
open (FILE, ">PromoterMatchtest.txt");

while (my $SNPS = <$SNPSFile>) {
   chomp ($SNPS);

   while (my $Cord = <$PromoterFile>) {
      chomp ($Cord);

      my @CordFile =split(/\s/, $Cord); 
      my $Lend = $CordFile[0];
      my $Rend = $CordFile[1];
      my $Promoter = $CordFile[4];
      my $SigmaFactor = $CordFile[7];

      foreach $a ($SNPS) 
      {
         if ($a >= $Lend && $a <= $Rend)
         {
            print FILE "$a\t$CordFile[4]\t$CordFile[7]\n";
         }
         else 
         { 
            print FILE "$a\n";
         }
      }
   }
}
close FILE;
close $PromoterFile;
close $SNPSFile;
exit;

到目前为止我的输出是这样的:

250
250     yaaAp1  Sigma70
250

调用文件 1 的第一行并循环文件 2 的地方。但是文件 2 的每一行都使用了 else 命令,并且脚本从不循环遍历文件 1 的其他行。

你的问题是你没有通过第二个文件重置你的进度。您从 $SNPSFile 中读取一行,对照第二个文件中的每一行进行检查。

但是当你重新开始时,你已经在文件末尾了,所以:

while (my $Cord = <$PromoterFile>) {

没有什么可读的。

一个快速解决方法是在其中添加一个 seek 命令,但这会使代码效率低下。我建议改为将文件 1 读入数组,然后引用它。

这是一份重写初稿,可能会有帮助。

#!/usr/bin/perl

use warnings;
use strict;

use Data::Dumper;

open my $PromoterFile, '<', 'promoterstest.txt'     or die $!;
open my $SNPSFile,     '<', 'NC_SNPtest.txt'        or die $!;
open my $output,       ">", "PromoterMatchtest.txt" or die $!;


my @data;

while (<$PromoterFile>) {
    chomp;
    my @CordFile    = split;
    my $Lend        = $CordFile[0];
    my $Rend        = $CordFile[1];
    my $Promoter    = $CordFile[4];
    my $SigmaFactor = $CordFile[7];

    push(
        @data,
        {   lend        => $CordFile[0],
            rend        => $CordFile[1],
            promoter    => $CordFile[4],
            sigmafactor => $CordFile[7]
        }
    );
}

print Dumper \@data;

foreach my $value (<$SNPSFile>) {
    chomp $value;
    my $found = 0;
    foreach my $element (@data) {
        if (    $value >= $element->{lend}
            and $value <= $element->{rend} )
        {
            #print "Found $value\n";
            print {$output} join( "\t",
                $value, $element->{promoter}, $element->{sigmafactor} ),
                "\n";
            $found++;
            last;
        }

    }
    if ( not $found ) {
        print {$output} $value,"\n";
    }
}

close $output;
close $PromoterFile;
close $SNPSFile;

首先 - 我们打开 file2,将其中的内容读入哈希数组。 (如果其中任何元素是唯一的,我们可以将其关闭。)

然后我们逐行读取 SNPSfile,查找每个密钥 - 如果存在则打印它(至少一次,在第一次点击时),如果不存在则仅打印该密钥。

这会生成输出:

250     yaaAp1  Sigma70
275     yaaAp1  Sigma70
375     yaaAp2  Sigma70

这是你的目标吗?

除了输出 @data 内容的 'Dumper' 语句外:

$VAR1 = [
          {
            'sigmafactor' => 'Sigma70',
            'promoter' => 'yaaX',
            'lend' => '20',
            'rend' => '100'
          },
          {
            'sigmafactor' => 'Sigma70',
            'promoter' => 'yaaAp1',
            'rend' => '300',
            'lend' => '200'
          },
          {
            'promoter' => 'yaaAp2',
            'sigmafactor' => 'Sigma70',
            'rend' => '400',
            'lend' => '350'
          }
        ];

这是我对编程解决方案的看法。重要的是

  • 使用词法文件句柄和open

  • 的三参数形式
  • 局部变量保持小写字母、数字和下划线

我还使用了 autodie pragma 来消除显式测试 open 状态的需要,以及核心库 List::Util 中的 first 函数到让代码更清晰简洁

use strict;
use warnings;
use 5.010;
use autodie;

use List::Util 'first';

my @promoters;
{
    open my $fh, '<', 'promoterstest.txt';
    while ( <$fh> ) {
        my @fields = split;
        push @promoters, [ @fields[0,1,4,7] ];
    }
}

open my $fh,     '<', 'NC_SNPStest.txt';
open my $out_fh, '>', 'PromoterMatchtest.txt';
select $out_fh;

while ( <$fh> ) {
    my ($num) = split;
    my $match = first { $num >= $_->[0] and $num <= $_->[1] } @promoters;
    if ( $match ) {
        print join("\t", $num, @{$match}[2,3]), "\n";
    }
    else {
        print $num, "\n";
    }
}

输出

250 yaaAp1  Sigma70
275 yaaAp1  Sigma70
375 yaaAp2  Sigma70