在perl中将新列附加到文件

时间:2014-11-12 15:32:48

标签: perl subroutine

我在perl脚本中有以下函数:

sub fileSize {
   my $file = shift;
   my $opt = shift;
   open (FILE, $file) or die "Could not open file $file: $!";
   $/ = ">";
   my $junk = <FILE>;
   my $g_size = 0;   
   while ( my $rec = <FILE> ) {
      chomp $rec; 
      my ($name, @seqLines) = split /\n/, $rec;
       my $sec = join('',@seqLines);
      $g_size+=length($sec);
      if ( $opt == 1 ) {
        open TMP, ">>", "tmp" or die "Could not open chr_sizes.log: $!\n";
        print TMP "$name\t", length($sec), "\n";
      }
   }
   if ( $opt == 0 ) {
      PrintLog( "file_size: $g_size", 0 );
   }
   else {
      print TMP "file_size: $g_size\n";
      close TMP;
   }
   $/ = "\n";
   close FILE;
}

输入文件格式:

>one
AAAAA
>two
BBB
>three
C

我有几个具有该格式的输入文件。以“&gt;”开头的行是相同的,但其他线可以是不同的长度。只有一个文件的函数输出是:

one 5
two 3
three   1

我想在每个文件的循环中执行该函数:

foreach my $file ( @refs ) {
   fileSize( $file, 1 );
}

运行下一次迭代时,假设使用此文件:

>one
AAAAABB
>two
BBBVFVF
>three
CS

我想获得这个输出:

one 5 7
two 3 7
three 1 2

如何修改功能或修改脚本才能获得此功能?可以看出,我的函数将文本附加到文件

谢谢!

2 个答案:

答案 0 :(得分:1)

您需要打开输出文件本身。首先处于读取模式,然后处于写入模式 我写了一个脚本来做你要求的。真正重要的是将新数据附加到旧数据的部分。将其改编为fileSize函数。

所以你有输出文件output.txt

表格中,

one 5
two 3
three 1

一系列输入文件input1.txtinput2.txt等保存在@inputfiles变量中。

表格中,

>one
AAAAA
>two
BBB
>three
C
>four
DAS

>one
AAAAABB
>two
BBBVFVF
>three
CS

分别

运行以下perl脚本后,

# First read previous output file.
open OUT, '<', "output.txt" or die $!;
my @outlines;
while (my $line = <OUT> ) {
    chomp $line;
    push @outlines, $line;
}
close OUT;
my $outsize = scalar @outlines;

# Suppose you have your array of input file names already prepared
my @inputfiles = ("input1.txt", "input2.txt");
foreach my $file (@inputfiles) {
    open IN, '<', $file or die $!;
    my $counter = 1; # Used to compare against output size
    while (my $line = <IN>) {
        chomp $line;
        $line =~ m/^>(.*)$/;
        my $name = $1;

        my $sequence =  <IN>;
        chomp $sequence;
        my $seqsize = length($sequence);

        # Here is where I append a column to output data.
        if($counter <= $outsize) {
            $outlines[$counter - 1] .= " $seqsize";
        } else {
            $outlines[$counter - 1] = "$name $seqsize";
        }
        $counter++;
    }
    close IN;
}

# Now rewrite the results to output.txt
open OUT, '>', "output.txt" or die $!;
foreach (@outlines) {
    print OUT "$_\n";
}
close OUT;

您生成输出

one 5 5 7
two 3 3 7
three 1 1 2
four 3

答案 1 :(得分:1)

我遗漏了你的选项和文件IO操作,并专注于通过命令行显示一个使用数组数组的方法。我希望它有所帮助。我将把它连接到你自己的脚本和子程序,主要取决于你:-)

针对您的第一个数据文件运行此一个衬垫:

perl -lne ' $name = s/>//r if /^>/ ; 
   push @strings , [$name, length $_] if !/^>/ ;
   END { print "@{$_ } " for @strings }' datafile1.txt

给出了这个输出:

one 5 
two 3 
three 1 

替换数据文件的第二个版本或实例(,其中记录one包含AAAAABB)也会给出预期结果。

one 7 
two 7 
three 2

在上面的脚本中,您将以此格式保存到输出文件。因此,要将列附加到输出文件中的每一行,我们可以以相同的方式处理每个数据文件(幸运的是,这可能意味着可以将事物转换为可在foreach中运行的函数环)。如果我们将转换后的数据保存为array of arrays(AoA),那么我们只需将push我们为每个数据文件字符串获取的length值放到相应的匿名数组元素上,然后打印出阵列。瞧!现在让我们希望它有效; - )

您可能希望安装Data::Printer,可以在命令行中使用-MDDP作为> /tmp/output.txt来可视化数据结构。

  • 首先 - 运行上面的脚本并将输出重定向到DDP
  • 的文件
  • 接下来 - 试试这个使用pperl -MDDP -lne 'BEGIN{ local @ARGV=shift; @tmp = map { [split] } <>; p @tmp } $name = s/>//r if /^>/ ; push @out , [ $name, length $_ ] if !/^>/ ; END{ p @out ; }' /tmp/output.txt datafile2.txt ` 的长单行显示我们创建的数组的结构:

    BEGIN

local区块中,我们@ARGV - ize shift; TMP关闭第一个文件(我们的{local @ARGV=shift}文件版本) - split几乎是用于处理多个输入文件的perl习惯用法;然后我们[]在匿名数组构造函数(map { })和@tmp内进入DDP数组,我们用p()的{​​{1}}显示功能。一旦我们离开BEGIN块,我们用perl的while (<>){ ... }命令行开关得到的隐式-n接管并从@ARGV读入剩余的文件;我们处理以>开头的行 - 剥离前导字符并将后面的字符串分配给$name变量; while继续,我们push $name以及不以length>)开头的任何行的if !/^>/包含为匿名数组[]进入@out数组,我们也会在p()块中显示END{}块,因此不会在隐式while()内打印环)。唷!

查看结果为gist @Github的AoA。

  • 最后 - 在此基础上构建,现在我们已经很好地完成了事情 - 我们可以在END{...}块中更改一些内容(向for周围添加嵌套push循环)并把它们放在一起产生我们想要的输出。

这一个班轮:

perl -MDDP -lne 'BEGIN{ local @ARGV=shift; @tmp = map {[split]} <>; }
$name = s/>//r if /^>/ ; push @out, [ $name, length $_ ] if !/^>/ ;
END{ foreach $row (0..$#tmp) { push $tmp[$row] , $out[$row][-1]} ; 
   print "@$_" for @tmp }'  output.txt datafile2.txt 

产生

one 5 7
two 3 7
three 1 2

我们必须将其转换为脚本: - )

该脚本由三个相当冗长的子程序组成,这些子程序读取日志文件;解析数据文件;合并它们。我们按顺序运行它们。第一个检查是否存在现有日志并创建一个,然后执行exit以跳过任何进一步的解析/合并步骤。

您应该能够将它们包装在某种循环中,这种循环将文件从数组提供给子例程,而不是从STDIN获取它们。一个警告 - 我正在使用IO::All因为它很有趣而且很容易!

use 5.14.0 ;          
use IO::All;    
my @file = io(shift)->slurp ;          
my  $log = "output.txt" ; 

&readlog;         
&parsedatafile;  
&mergetolog;   

####### subs ####### 
sub readlog {
   if (! -R $log) {
     print "creating first log entry\n";
     my @newlog = &parsedatafile ;  
     open(my $fh, '>', $log) or die "I CAN HAZ WHA????" ;  
     print $fh "@$_ \n" for @newlog ;
     exit;
   }
   else {
     map { [split] } io($log)->slurp ;
   }
}

sub parsedatafile {   
  my (@out, $name) ;     
  while (<@file>) {   
    chomp ;       
    $name = s/>//r if /^>/;   
    push @out, [$name, length $_] if !/^>/ ;   
  } 
  @out;       
} 

sub mergetolog {   
  my @tmp = readlog ;     
  my @data = parsedatafile ;  
  foreach my $row (0 .. $#tmp) { 
    push $tmp[$row], $data[$row][-1]  
  }        
  open(my $fh, '>', $log) or die "Foobar!!!" ; 
  print $fh "@$_ \n" for @tmp ;  
}   

子程序在这里完成所有工作 - 你可能会找到缩短的方法;结合;改善它们。这对你来说是一个有用的方法吗?

我希望这个解释对某人来说是明确和有用的 - 欢迎更正和评论。可能同样的事情可以通过地点编辑(perl -pie '...')完成,这可以作为练习留给后面的人......