Perl:读取文件,抓取特定行并编写新文件

时间:2016-02-11 07:50:03

标签: perl

我有一个输入文件,格式如下:

ATOM      1  CAY GLY X   1      -0.084   0.026  -0.058  1.00  2.67      PEP   
ATOM      2  HY1 GLY X   1      -0.448   1.075  -0.037  1.00  0.00      PEP 
.....
END
ATOM      1  CAY GLY X   1      -0.084   0.026  -0.058  1.00  2.67      PEP   
ATOM      2  HY1 GLY X   1      -0.448   1.075  -0.037  1.00  0.00      PEP
.....
END

此模式重复1000次。我想读取输入文件并将ATOM和END之间的所有行打印到具有唯一名称的输出文件(即output001.pdb)。需要重复进行此过程,直到读取输入文件的所有行。 示例输出文件看起来像这样(output001.pdb):

ATOM      1  CAY GLY X   1      -0.084   0.026  -0.058  1.00  2.67      PEP   
ATOM      2  HY1 GLY X   1      -0.448   1.075  -0.037  1.00  0.00      PEP
.....
END

到目前为止,这是我的代码:

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

my $input = 'all.pdb';

open my $input_fh, '<', $input or die $!;

my @lines;
my @fh;
while ( <$input_fh> ) {
    chomp;
    if ($lines =~ m/ATOM/ .. m/END/ ) {
    for my $i (1 .. 1000) {
    open $fh[$i], '>', "file-$i" or die $!;
 }       
    print {$fh[$i]} $lines;
    }
 } 
 close ($fh[$i]);
 close ($input_fh);

我不确定给定数组的匹配语句是否正确。任何有关改进的建议都非常感谢。

2 个答案:

答案 0 :(得分:1)

试试这个。

是否要将END的原子记录与输入文件分开,只需使用input record separator 即可。 然后你看后面或\K来保持分裂的话。

use warnings;
use strict;
open my $handler, "input.pdb";
local $/;
my @file = split(/(?<=END)/, <$handler> );
my $i = 0;


while ($i < $#file)
{
    open my $write,">","output$i.pdb";
    $file[$i]=~s/^\n//g;
    print $write $file[$i];
    $i++;
}

答案 1 :(得分:1)

根据你的方向建立一个有点直截了当的方法如下:

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

my $input = 'all.pdb';
my $file_count = 0;
my $taking_lines = 0;
my $output_fh;

open my $input_fh, '<', $input or die $!;

while ( <$input_fh> ) {
    if (m/ATOM/ and ! $taking_lines) {
      $file_count++;
      my $output_filename = "output" . sprintf("%0.3i", $file_count) . ".pdb";
      open $output_fh, '>', $output_filename or die $!;
      $taking_lines = 1;
    }

    print $output_fh $_ if $taking_lines;

    if (m/END/ and $taking_lines) {
      close $output_fh;
      $taking_lines = 0;
    }
 } 

close $output_fh if $taking_lines ;
print "Created $file_count files.\n" ;

这比某些解决方案稍长一些,但可以说更容易理解并因此保持。如果找到 ATOM 并且我们还没有排队,我们只需构建新文件名,打开新文件并设置一个标志($ taking_lines)。如果我们找到 END 并且我们当前正在占线,我们会关闭该文件并清除该标记。

在两者之间,如果设置了标志,我们只需打印到输出文件。列出这三个动作的顺序很重要。 ATOM的检查必须在输出打印之前,否则我们将错过第一行(使用ATOM)。在输出打印之后,END的检查必须是,否则我们会提前关闭文件并错过END行。

如果您查看“perldoc opentut”(意思是“打开教程”),您将了解 实际上可以重新使用文件句柄的方式 - 也就是说,您可以通过&amp; open a现有的,正在使用的文件句柄和perl将关闭“旧”文件,并毫不费力地打开“新”文件。这开辟了缩短脚本的方法(就像最后一个'if'语句可能只是$taking_lines = 0 if m/END/; - 但是,再次可以说,对于那些因维护代码而不一定使用这些技巧而变得更加容易的人来说,它更简单容易。 / p>