如何为字符串模式的所有实例打印下N行?

时间:2013-02-28 07:18:13

标签: regex arrays perl parsing

我有一个如下所示的文件:

文件

variableStep chrom=chr1 span=25
10076   0.84
10101   1
10126   1
10151   1
10176   1
10201   1
10226   1.72
variableStep chrom=chr1 span=25
10251   2
10276   1.16
10301   1
10326   1
10351   1
10376   1
10401   1
10426   0.28
11451   0.04
variableStep chrom=chr2 span=25
9781451     2
19781476    2
19781501    2
19781526    2
19781551    1
19781576    1
19781601    0.48
variableStep chrom=chr2 span=25
19781826    0.28
19781851    1
19781876    1
19781901    1
19781926    1
19781951    1.48
19781976    3.68
19782001    4.56
19782026    4
variableStep chrom=chr3 span=25
4813476 1
24813501    1
24813526    1
24813551    1
24813576    1.88
24813601    2
variableStep chrom=chr3 span=25
24813626    1.4
24813651    1.48
24813676    2
24813701    2
24813726    2
24813751    2
variableStep chrom=chr4 span=25
24815401    2.24
24815426    3
24815451    3
24815476    3
24815501    3
24815526    2.04
variableStep chrom=chr4 span=25
24815551    2
24815576    1.76
24815601    0.76
24815951    0.48
24815976    1
24816001    1
24816026    1
24816051    1
variableStep chrom=chr5 span=25
24817226    0.92
24817251    1.48
24817276    3
24817301    3
variableStep chrom=chr5 span=25
24817326    3
24817351    3
24817376    3
24817401    3.04
24817426    3.08

需要什么

我需要做的是,对于所有说variableStep chrom=chr1 span=25的实例,将后续n行打印到输出文件。 n我必须提到,变化很大。在实际文件中,它可以在300,000到500,000+之间变化。

所需输出

1.Output_file_1_for_variableStep chrom = chr1 span = 25

10076   0.84
10101   1
10126   1
10151   1
10176   1
10201   1
10226   1.72
10251   2
10276   1.16
10301   1
10326   1
10351   1
10376   1
10401   1
10426   0.28
11451   0.04

2._Output_file_2_for_variableStep chrom = chr2 span = 25

9781451     2
19781476    2
19781501    2
19781526    2
19781551    1
19781576    1
19781601    0.48
19781826    0.28
19781851    1
19781876    1
19781901    1
19781926    1
19781951    1.48
19781976    3.68
19782001    4.56
19782026    4

3._Output_file_3_for_variableStep chrom = chr3 span = 25

4813476     1
24813501    1
24813526    1
24813551    1
24813576    1.88
24813601    2
24813626    1.4
24813651    1.48
24813676    2
24813701    2
24813726    2
24813751    2

4._Output_file_4_for_variableStep chrom = chr4 span = 25

24815401    2.24
24815426    3
24815451    3
24815476    3
24815501    3
24815526    2.04
24815551    2
24815576    1.76
24815601    0.76
24815951    0.48
24815976    1
24816001    1
24816026    1
24816051    1

5._Output_file_5_for_variableStep chrom = chr5 span = 25

24817226    0.92
24817251    1.48
24817276    3
24817301    3
24817326    3
24817351    3
24817376    3
24817401    3.04
24817426    3.08

背景
我仍然认为自己是一个Perl新手,所以我写的代码并没有完全完成任务。

事实上,以下代码描述了我试图让它发挥作用的3种方式。对于模式为variableStep chrom=chr1 span=25的代码,我尝试在手动执行正则表达式匹配后打印后续行。

根据我的想法,我需要一个循环来遍历所有后续行,这就是我用模式variableStep chrom=chr1 span=25编写的。但后来,我意识到我需要一个退出机制,否则所有后续行都会被打印出来。

this exit pattern写为last if /^v.*$/,我需要弄清楚。因为我现在只打印特定模式的first实例。没有空行我可以退出。如果我有一个空行,这段代码工作得很好(修改为last if /^$/)。我甚至尝试使用非小数字符作为/^\D.*$/,但它不起作用。 What exit pattern should I use?

代码的剩余部分是我的宝贝尝试让程序运行,它只在模式匹配后打印单个后续行。

代码

#Trial code to parse main file
use 5.014;
use warnings;

#Assign filename
my $file = 'trial.txt';

#Open filename
open my $fh, '<' , $file || die $!;

#Open output
open OUT1, ">Trial_chr1.out" || die $!;
open OUT2, ">Trial_chr2.out" || die $!;
open OUT3, ">Trial_chr3.out" || die $!;
open OUT4, ">Trial_chr4.out" || die $!;
open out5, ">Trial_chr5.out" || die $!;

#Read in file
while(<$fh>){
    chomp;
    if (/^variableStep chrom=chr1 span=25/){

        my $nextline1 = <$fh>;#means next line after pattern match
        my $nextline2 = <$fh>;
        my $nextline3 = <$fh>;
        my $nextline4 = <$fh>;
        my $nextline5 = <$fh>;
        my $nextline6 = <$fh>;
        my $nextline7 = <$fh>;
        print OUT1 $nextline1;
        print OUT1 $nextline2;
        print OUT1 $nextline3;
        print OUT1 $nextline4;
        print OUT1 $nextline5;
        print OUT1 $nextline6;
        print OUT1 $nextline7;

    }elsif(/^variableStep chrom=chr2 span=25/){

        my @grabbed_lines; #Initialize array to store lines after pattern match
        while (<$fh>){ #Read subsequent lines while in a loop

        last if /^v.*$/; #Break out of the loop if line encountered begins with v
        push @grabbed_lines, $_;# As long as the above condition is false, push the lines into the array

        }print OUT2 @grabbed_lines; # Print the grabbed lines

    }elsif(/^variableStep chrom=chr3 span=25/){
        my $nextline = <$fh>;
        print OUT3 $nextline;

    }elsif(/^variableStep chrom=chr4 span=25/){
        my $nextline = <$fh>;
        print OUT4 $nextline;
    }elsif(/^variableStep chrom=chr5 span=25/){
        my $nextline = <$fh>;
        print out5 $nextline;
    }
}


#Exit
exit;

感谢您抽出宝贵时间来回答我的问题。我会很感激任何提示和建议。

4 个答案:

答案 0 :(得分:2)

好的,我很想念n部分,每场比赛都有所不同,经过测试和运作:

my $found = 0;

while (<$fh>) {
    if ( $found && /^\d/ ) {
        print $_;
    }
    else {
        $found = 0;
    }

    if (/^variableStep chrom=chr2 span=25/) {
        $found = 1;
    }
}

这样它会打印以数字开头的所有后续行。

<强>解释

这里的问题是,每次调用<$fh>时它都会读取下一行,所以如果你测试行内容而你的测试失败了,你就不应该进行下一个循环,因为那样下一行被读取,你丢失了测试失败的行。

所以我来到这个解决方案:

  1. 我使用标志知道我在哪种模式下,是否要搜索要打印的行?

  2. 仅输入第一个if

    1. 如果我在第二个中,如果在之前的循环中并且标志已设置为“1”

    2. 并且该行以数字开头。

  3. 当此测试失败时,即开头没有带数字的行时,我重置了标志,如果它以“variableStep ...”开头,则有机会再次查看同一行。 / p>

答案 1 :(得分:1)

下面的Oneliner应该这样做(假设输出文件尚不存在):

perl -lne '/variableStep/ && open($fh, ">>", $_) && next; print $fh $_;' input.txt

-

btw:||运算符具有高优先级(man perlop),因此命令:

open OUT1, ">Trial_chr1.out" || die $!;

被perl理解为

open OUT1, (">Trial_chr1.out" || die $!);

要进行错误检查,您应该使用and运算符,或使用括号来强制执行所需的行为

答案 2 :(得分:0)

我喜欢,但在这种情况下更合适,请参阅:

$ awk '
    {if ($0 ~ /^variableStep/) {file="output_file_"++c"_"$1"_"$2"_"$3}
    else{print $0 > file}}
' file.txt

$ ls -l output_file_*

答案 3 :(得分:0)

使用File::Slurp有用的模块:

use strict; use warnings;
use File::Slurp;

my ($c, $file);

while (<>) {
    if (/^variableStep\s+chrom=\w+\s+span=\d+/) {
        $c++;
        $file = $&;
        $file =~ s/\s/_/g;
        $file = "output_file_${c}_" . $file;
    }
    else {
        append_file $file, $_;
    }
}

用法:

$ perl ./script.pl file.txt
$ ls -l output_file_*