我有一个我想过滤的文件,就像那样:
##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函数直到找到空行,但它不起作用。 如果有人可以帮助我...... 非常感谢你提前, 瓦西利斯。
答案 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
。