我有一个想要过滤的文件,如下所示:
##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因此,简单地说,有一个空行分隔的块。每个块都以行“chain 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函数,直到会找到空行,但它不起作用。如果有人能帮我弄一下那个…。提前谢谢你,Vasilis。
发布于 2014-04-07 02:50:59
这里的第一个问题是' '是一个空格,而不是空行(""或''应该没问题,因为您已经chomp-ed了该行。
第二个问题是
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
}
}发布于 2014-04-07 04:03:22
通常情况下,最好不要使用unless而不是while。它多次否定布尔表达式,给您留下了一个双重否定的问题要解决。下面是一个例子
while ( $foo ne $bar ) {即使这是一个否定的表达式,我也可以很容易地确定何时退出我的循环。但是:
until ( $foo eq $bar ) {只是需要时间来弄清楚。
此外,‘’不会生成空行:请使用正则表达式$cline =~ /^\s*$/。然而,即使在此基础上,循环:
until ($cline ne ' ') {
push (@array, $cline);
}如果$cline等于空,它将永远运行。您永远不会更改$cline的值。
您可以使用我用来称为状态变量的方法(直到Perl实际创建了一个名为state的变量类型,而现在我不知道该如何调用它们)。此变量跟踪您在文件中的位置。你是在文件的链节里吗?你到底想不想要这些行?这样,您只有一个循环。您设置状态变量,然后处理您的循环。
在本例中,我有一个名为$keep_line的状态变量,它询问我是否希望保留要读入的行。如果行以chain开头,并且第二个字段大于3000,我希望保留整个块(如果我理解您要做什么的话)。(顺便说一句,我保留了空行。这样可以吗?)
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;
}
}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;
}
}大大简化了代码。
我还会声明一些常量来删除那些神秘的Moes。像3000和/^chain/这样的东西在你的程序中有着神秘而又重要的意义。您可以使用use constant作为编译指示来定义Perl常量。它是标准Perl的一部分:
use constant {
KEEP_LIMIT => 3000,
SECTION_HEADER => qr/^chain/,
};现在,我可以这样做:
if ( $line =~ SECTION_HEADER ) {而不是:
if ( $line =~ /^chain/ ) {和
if ( $field[1] > KEEP_LIMIT ) {而不是
if ( $field[1] > 3000 ) {constant杂注有问题。最大的问题是,它不会在Perl通常会对变量进行插值的地方进行插值。这包括双引号字符串和散列键。如果我有$foo{KEEP_LIMIT},Perl会将键解释为字符串KEEP_LIMIT,而不是KEEP_LIMIT的常量。
许多开发人员使用Readonly,它在很多方面都要好得多。所以,我倾向于使用constant。
https://stackoverflow.com/questions/22897997
复制相似问题