如何读取文件并为每一行创建记录

时间:2010-12-07 04:36:42

标签: arrays perl function hash bioinformatics

寻找有关编写Perl程序的帮助,该程序接受输入文件并根据后续命令执行操作。我是Perl的初学者,所以请不要过于提高建议。到目前为止,我所拥有的结构是一个主程序和4个子程序。

我遇到两部分问题:

  
      
  1. 编写主段的一部分,为输入文件中的每一行创建一个唯一记录(固定宽度格式)。我认为这应该用substr完成,但我不知道应该如何构建它。到目前为止,解包已经超出了我的学习范围。

  2.   
  3. 主程序中调用的一个函数是“距离”子,它将计算原子之间的距离。我认为这应该是For循环中的For循环。我应该采取什么方法的想法?

  4.   

记录应该存储一个原子记录数组(每个换行符一个记录/原子):

•原子的序列号,5位数。 (第7 - 11栏)

•它所属氨基酸的三个字母的名称(第18-20栏)

•原子的三个坐标实数为十进制和&正交坐标(x,y,z)(列31-54)
对于X in Angstroms cols。 31-38
对于Ys in Angstroms cols。 39-46
对于Zs in Angstroms cols。 47-54

•原子的单字母或双字母元素名称(例如C,O,N,Na)(cols 77-78)

子距离     #取一组原子记录并返回最大距离
    #在该数组中的所有原子对之间#。 (第31-54栏)

以下是输入文件中的示例文本。

# truncating for testing purposes. Actual data is aprox. 100 columns     
# and starts with ATOM or HETATM    
__DATA__   
ATOM   4743  CG  GLN A 704      19.896  32.017  54.717  1.00 66.44           C    
ATOM   4744  CD  GLN A 704      19.589  30.757  55.525  1.00 73.28           C    
ATOM   4745  OE1 GLN A 704      18.801  29.892  55.098  1.00 75.91           O  

这是我到目前为止制作记录的主要内容和次要内容。我讨厌跛脚,但我没有任何东西可以显示为距离子,所以不要担心提供代码,任何关于如何处理的建议将非常感激。

use warnings;
use strict; 

my @fields;
my @recs;

while ( <DATA> ) {
chomp;
@fields = split(/\s+/);
push @recs, makeRecord(@fields);
}

for (my $i = 0; $i < @recs; $i++) {
printRec( $recs[$i] );
}
my %command_table = (
  freq => \&freq,
  length => \&length,
  density => \&density,
  help => \&help, 
  quit => \&quit
);

print "Enter a command: ";
  while ( <STDIN> ) {
  chomp; 
  my @line = split( /\s+/);
  my $command = shift @line;
  if ($command !~ /^freq$|^density$|length|^help$|^quit$/ ) {
    print "Command must be: freq, length, density or quit\n";
  }
    else {
    $command_table{$command}->();
  }
print "Enter a command: ";
}

sub makeRecord 
# Read the entire line and make records from the lines that contain the 
# word ATOM or HETATM in the first column. Not sure how to do this:
{
 my %record = 
 (
 serialnumber => shift,
 aminoacid => shift,
 coordinates => shift,
 element  => [ @_ ]
 );
 return\%record;
 }

3 个答案:

答案 0 :(得分:1)

您的记录具有固定宽度格式,因此请使用unpack将每条记录分成感兴趣的字段。使用每个字段的指定列位置构建模板以与unpack一起使用。

my @field_specs = (
    {begin =>  7, end => 11, name => 'serialnumber'},
    {begin => 18, end => 20, name => 'aminoacid'},
    {begin => 31, end => 38, name => 'X'}, 
    {begin => 39, end => 46, name => 'Y'},
    {begin => 47, end => 54, name => 'Z'}, 
    {begin => 77, end => 78, name => 'element'},
);
my $unpack_template;    
my @col_names;
for my $spec (@field_specs) {
    my $offset = $spec->{begin} - 1;
    my $width  = $spec->{end} - $offset;
    $template .= "\@${offset}A$width";
    push @col_names, $spec->{name};
}
print "Ready to read @col_names\n using template $template ...\n";

# prints 
# Ready to read serialnumber aminoacid X Y Z element 
#  using template @6A5@17A3@30A8@38A8@46A8@76A2 ...

my @recs;
while ( <DATA> ) {                
    my %record;
    @record{@col_names} = unpack($unpack_template, $_);    
    push @recs, \%record;                
}        

答案 1 :(得分:1)

奇怪的是,当我看到使用调度表时,unpack超出了范围。如果正在处理固定格式文件,则忽略使用unpack是愚蠢的。以下代码中没有任何“高级”内容:

use strict;
use warnings;
use Data::Dump 'dump';   # Use this if you want 'dump' function to work

my @records;
while ( my $record = <DATA> ) {

    next unless $record =~ /^ATOM|^HETATM/;  # Skip unwanted records

    # unpack minimizes the amount of work the code has to do ...
    # ... especially since you only want a small part of the file
    # 'x' tokens are ignored, 'A' tokens are read ...
    # The number following each token represents repetition count ...
    # ... so in this case the first 6 characters are ignored ...
    # ... and the next 5 are assigned to $serNo

    my ( $serNo, $aminoAcid, $xCoord, $yCoord, $zCoord )
        = unpack 'x6A5x6A3x10A10A10A10', $record;        # Get only what you want

    # Assign data to a hash reference

    my $recordStructure = {
                            serialnumber => $serNo,
                            aminoacid    => $aminoAcid,
                            coordinates  => [ $xCoord, $yCoord, $zCoord ],
                          };

    push @records, $recordStructure;  # Append current record
}

# 'dump' is really useful to view data structures. No need for PrintRec!!

dump @records;

答案 2 :(得分:1)

在线可以使用Perl代码处理PDB文件(显然你正在做)。我不是建议你只使用你下载的模块并完成它,因为你的教练肯定不会批准,你也不会学到那么多;)但是你可以看看提供的一些代码和试着看看那里有些位可以解决你的问题。

我做了一下谷歌搜索,我看到有ParsePDB.pm(例如)。您可以找到网页here。我没看过代码或功能,我只是希望那里有一些你可能会觉得有用的东西。

编辑1

好的,现在已经14个小时了,我觉得要做一些编码,所以你还没有接受答案我认为我可以忽略自己的建议并起草一些东西(你会发现我已经复制了Zaid的数据结构)......

#!/usr/bin/perl

use warnings;
use strict;

sub makeRecord {
   my ($ser_num, $aa, $x, $y, $z, $element) = @_;
   # copying Zaid now as her/his structure looks very sensible!
   my $record = {
                  serial  => $ser_num,
                  aa      => $aa,
                  element => $element,
                  xyz     => [$x, $y, $z],
                };
   return $record;
}


my $file = shift @ARGV;
my @records; # will be an array of hash references

open FILE, "<$file" or die "$!";
while (<FILE>) {
   if (/^ATOM|^HETATM/) { # only get the structure data lines
      chomp; # not necessary here, but good practice I'd say

      my @fields = split; # by default 'split' splits on whitespace

      # now use an array slice to only pass the array elements
      # you're interested in (using the positional indices from @fields):
      push @records, makeRecord(@fields[1,3,6,7,8,11]);
   }
}
close FILE;

编辑2

关于距离子程序:for循环中的for循环应该完成这项工作,但这是一种蛮力方式,可能需要很长时间(因为你必须做(number_of_atoms)^ 2计算),具体取决于关于输入分子的大小。为了你的任务,蛮力方法可能是可以接受的;在其他情况下,您必须决定是否支持编码的简易性或计算速度。如果你的导师也想让你记住后者,你可以看看this page(我知道你真的想要最大的距离,而你是3D,而不是2D ......)

好的,现在我只希望你能在这里找到一些有用的点点滴滴:)