【问题标题】:How to print next N lines for all instances of a string pattern?如何为字符串模式的所有实例打印下 N 行?
【发布时间】:2013-02-14 07:12:24
【问题描述】:

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

文件

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;

感谢您抽出宝贵时间回答我的问题。如有任何提示和建议,我将不胜感激。

【问题讨论】:

    标签: regex arrays perl parsing


    【解决方案1】:

    好的,我误解了n 部分,每场比赛都不同,这是经过测试和工作的:

    my $found = 0;
    
    while (<$fh>) {
        if ( $found && /^\d/ ) {
            print $_;
        }
        else {
            $found = 0;
        }
    
        if (/^variableStep chrom=chr2 span=25/) {
            $found = 1;
        }
    }
    

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

    说明:

    这里的问题是,每次调用&lt;$fh&gt; 时它都会读取下一行,因此如果您测试行内容并且测试失败,则不应执行下一个循环,因为这样会读取下一行你丢失了测试失败的那一行。

    所以我想到了这个解决方案:

    1. 我使用一个标志来了解我处于哪种模式,我是否正在搜索要打印的行?

    2. 只输入第一个if

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

      2. AND 行以数字开头。

    3. 当此测试失败时,即没有以数字开头的行,我重置标志,然后有机会再次查看以“variableStep ...”开头的同一行

    【讨论】:

    • N 不幸的是变化很大 :( 也许我应该编辑我的问题以突出这一方面。在大约 1GB 大的实际文件的子集中,n 的范围为 300,000-500,000我尝试过的几种模式。
    • @Neal,对不起,我错过了那部分,我改变了我的答案,它未经测试,但应该像这样工作。
    • @Neal,我再次更改了我的解决方案,这次它已经过测试并正在运行。
    • 嘿,这真的很棒。如果我正确理解了整体逻辑,我认为您使用的是flag variable?从广义上讲,我认为$found 的值设置为 0(false),当找到模式匹配时,值更改为 1(true) 并打印后续数字?你能不能解释一下逻辑和控制流线,因为我不知道为什么if (/^variableStep chrom=chr2 span=25/)必须在之后写.. :(
    • @Neal,我添加了一个解释。我不确定,但我认为您可以更改两个 if 的顺序,目前没有时间测试它。
    【解决方案2】:

    下面的 Oneliner 应该可以解决问题(假设输出文件尚不存在):

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

    --

    顺便说一句:|| 运算符具有高优先级(man perlop),所以命令:

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

    被perl理解为

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

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

    【讨论】:

    • 你好 otokan!感谢您的回答。这对我来说是一种非常新颖的完成工作的方式。我可以在哪里阅读有关命令行单行代码的更多信息?另外,不幸的是,在 cmd 终端中键入上述命令时,我遇到了这个神秘的错误。 Can't find string terminator "'" anywhere before EOF at -e line1。我可能做错了什么?
    • 你好 otokan!一个小问题,这仅适用于 UNIX 系统吗?因为我在 UNIX 中输入了命令,它工作得非常好......好吧不是确切的,最简单的一个衬里perl -e 'print "hello world\n"'。相同的命令在 Windows cmd 终端中产生错误。
    【解决方案3】:

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

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

    【讨论】:

    • 您好,非常感谢您的友好回答。但是,我仍在学习 Perl 的基本原理......甚至没有接触过 awk :(也许有一天,我也会有机会研究 awk
    【解决方案4】:

    使用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_*
    

    【讨论】:

    • 你测试过这个尼尔吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-30
    • 2013-04-27
    相关资源
    最近更新 更多