如何以适当的方式使用until函数

时间:2014-04-06 18:17:49

标签: perl

我有一个我想过滤的文件,就像那样:

##matrix=axtChain 16 91,-114,-31,-123,-114,100,-125,-31,-31,-125,100,-114,-123,-31,-114,91
##gapPenalties=axtChain O=400 E=30
chain 21455232 chr20 14302601 + 37457 14119338 chr22 14786829 + 3573 14759345 1
189     159     123
24      30      22
165     21      20
231     105     0
171     17      19
261     0       2231
222     2       0
253     56      48

chain 164224 chr20 14302601 + 1105938 1125118 chr22 14786829 + 1081744 1100586 8
221     352     334
24      100     112
34      56      56
26      50      47
…………………….
chain 143824 chr20 14302601 + 1105938 1125118 chr22 14786829 + 1081744 1100586 8

因此,简单地说,有一个空格分隔的块。 每个块都以第"行开头。链xxxxx"并继续使用数字线。 我想过滤掉文件,只保留带链的块,后面的数字大于3000。 我写了以下脚本来做到这一点:

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

my $chain = $ARGV[0];

#It filters the chains with chains >= 3000.
open my $chain_file, $chain or die "Could not open $chain: $!";

my @array;
while( my $cline = <$chain_file>)  {
    #next if /^\s*#/;
    chomp $cline;
    #my @lines = split (/ /, $cline);
    if ($cline =~/^chain/) {
        my @lines = split (/\s/, $cline);
        if ($lines[1] >= 3000) {
            #print $lines[1];
            #my @lines = split (/ /, $cline);

            #print "$cline\n";
            push (@array, $cline);
        }
    }
    until ($cline ne ' ') {
        push (@array, $cline);
    }


    foreach (@array) {
        print "$_\n";

    }
    undef(@array);
}

问题是我只能打印标题(链XXXXX ......)而不是每个块的下一行后面的数字。 我使用until函数直到找到空行,但它不起作用。 如果有人可以帮助我...... 非常感谢你提前, 瓦西利斯。

2 个答案:

答案 0 :(得分:1)

这里的第一个问题是' '是一个空格,而不是一个空行(""''应该没问题,因为您已经chomp - 线。

第二个问题是

until ( $cline ne "" )

相同
while ( $cline eq "" )

与您将行推送到@array所需的内容相反。

那就是说,触发器操作符可能是一个更合适的结构,用于你之后:

my @array;
while ( <$chain_file> ) {          # Using $_ instead of $cline

    chomp;

    if ( do { /^chain\s+(\d+)/ && $1 >= 3000 } .. /^$/ ) {

                                   # Accumulate lines in @array
        push @array, $_;           # False until LHS evaluates to true ...
    }                              # ... then true until RHS evaluates to true

    else {
        for ( @array ) {
          print $_, "\n";          # Print matches
        }
        @array = ();               # Reset/clear out @array
    }
}

答案 1 :(得分:0)

通常最好不要使用unless代替while。它多次否定布尔表达式,让你得到双重否定来解决。这是一个例子

 while ( $foo ne $bar ) {

尽管这是一个否定的表达,我可以很容易地找出何时退出循环。但是:

until ( $foo eq $bar ) {

花点时间搞清楚。

此外,&#39; &#39;不会出现空白行:使用正则表达式$cline =~ /^\s*$/。然而,甚至超越了循环:

until ($cline ne ' ') {
    push (@array, $cline);
}
如果$cline等于空白,

将永远持续下去。您永远不会更改$cline的价值。

你可以使用我用来调用状态变量(直到Perl实际创建了一个名为state的变量类型,现在我不知道该怎么称呼它们。)这个变量跟踪在哪里你在你的档案里。你在文件的链部分吗?你想要这些线路吗?这样,您只有一个循环。您可以设置状态变量,然后处理循环。

在这个例子中,我有一个名为$keep_line state 变量,它询问我是否要保留我想要读入的行。如果该行以{{ 1}}并且第二个字段大于3000,我想保留整个块(如果我理解你正在尝试做什么)。 (顺便说一句,我保留了空白。这样可以吗?)

chain

我在这里也闻到了一个函数:我可能会把这个函数返回给我设置my $keep_lines = 0; # Aren't in lines I want to keep my @good_lines; # Array where you want to keep the good lines while ( my $line = <$chain_file> ) { chomp $line; # Good habit to always chomp a input as soon as it's read. if ( $line =~ /^chain/ ) { # This is a chain line. Do I want to keep this group? my @fields = ( /\s+/, $line ); if ( $field[1] > 3000 ) { # Yes, if the second field is greater than 3000 $keep_lines = 1; # Keep this and all other lines until the next chain line } else { $keep_lines = 0; # Skip until the next chain line } } if ( $keep_lines ) { push @good_lines, $line; } } 的值,而不是if子句的纠结。

$keep_lines

简化代码。

我还会声明一些常量来删除那些神秘的Moes 。那些像while ( my $line = <$chain_file> ) { chomp $line; # Good habit to always chomp a input as soon as it's read. $keep_lines = keep_this_section( $line ); if ( $keep_lines ) { push @good_lines, $line; } } 3000这样的东西在你的程序中有某种神秘但重要的含义。您可以/^chain/作为pragma来定义Perl常量。它是标准Perl的一部分:

use constant

现在,我可以做这样的事情:

use constant {
    KEEP_LIMIT       => 3000,
    SECTION_HEADER   => qr/^chain/,
};

而不是:

if ( $line =~ SECTION_HEADER ) {

if ( $line =~ /^chain/ ) {

而不是

if ( $field[1] > KEEP_LIMIT ) {

constant pragma存在问题。最大的问题是,它不会在Perl通常插入变量的地方进行插值。这包括双引号字符串和散列键。如果我有if ( $field[1] > 3000 ) { ,Perl会将密钥解释为字符串$foo{KEEP_LIMIT},而不是KEEP_LIMIT的常量。

许多开发人员使用Readonly,这在很多方面都要好得多。遗憾的是,KEEP_LIMIT不是标准的Perl模块,因此您必须通过CPAN进行安装,而且有时无法或不希望这样做。所以,我倾向于使用Readonly