如何在Perl中使用正则表达式匹配后打印行?

时间:2013-02-13 03:43:39

标签: regex perl

我正在解析格式如下的文本文件

> alpha
apple
airplane
art
> beta
bear
blue
beat
> charlie
cow
cent
coat

我正在尝试查找具有主题的条目(alpha beta charlie)(例如“ta”)。如果找到了主题,那么我正在尝试打印每个条目下面的单词(bear blue beat)。 所以在这个例子中,我想要以下输出

> beta
bear
blue
beat

我已经弄明白了如何打印输入行,但不知道如何打印下面的行。任何想法都会非常感激。

my $motif = "ta";
my $file = "file.pl";
open(INPUT, $file) or die "Can't open file.\n";
parse($motif);

sub parse{
    my ($x) = (@_);
    while(<INPUT>){
        if($_ =~ />*($x)/){
            print $_."\n";
#       if($_ !~ />/){
#           print $_."\n";
        }else{
            next;
        }
    }
}

5 个答案:

答案 0 :(得分:4)

这是另一种选择:

use strict;
use warnings;

my $motif = pop;
local $/ = '>';    # record separator

while (<>) {
    chomp;
    print $/ . $_ if /(?<=\x20).*?$motif/;
}

用法:perl script.pl data.txt 'ta'

data.txt中的数据输出:

> beta
bear
blue
beat

符号local $/ = '>'将记录分隔符设置为>而不是通常的\n,因此每个>都标记了已读取的记录的开头。您搜索的行在>后面会有一个空格,这就是正面的(?<=\x20)尝试匹配的内容。

您可以通过执行以下操作将输出重定向到文件:perl script.pl data.txt 'ta' >output.txt

答案 1 :(得分:2)

好吧,你需要保持循环状态。匹配触发“打印”状态,不匹配则触发它。所以你有类似的东西吗?

sub parse {
  my ($x) = (@_);
  my $printable = 0

  while (<INPUT>) {
    if ($_ =~ /^>.*($x)/) {
      print $_;
      $printable = 1;
    } elsif ($_ =~ /^>/) {
      $printable = 0;
    } elsif ($printable) {
      print $_;
    }
  }
}

答案 2 :(得分:1)

基本上,您可以使用变量($print_flag)来查看是否应该打印该行。如果你得到你想要的匹配,它被设置为1(被评估为真),如果你匹配以">"开头但不包含$motif的行,则设置为0。

试试这个(警告:未经测试):

use strict;    #Always!
use warnings;  #Always!
my $motif = "ta";
my $file = "file.pl";
open(my $input,"<", $file) or die $!; #two argument open: bad!
parse($motif);

sub parse{
    my ($x) = (@_);
    my $print_flag = 0;
    while(<$input>){

        chomp; #removes trailing EOL character
        if(/^>.*($x)/)   #Don't need $_
        {    
            $print_flag = 1;
        }
        elsif(/^>/)
        {
            $print_flag = 0;
        }

        if($print_flag)
        {
            print "$_\n";
        }
    }
}

答案 3 :(得分:1)

对于Perl范围操作符来说,这种类型的问题似乎很自然(在这种情况下,更常见的是,作为“触发器”操作符)。但是,您的要求的详细信息最终使它比我预期的要复杂得多:

#!/usr/bin/env perl    

use strict;
use warnings;

parse('ta');

sub parse {
  my $pattern = shift;

  my $seq;
  while (<DATA>) {
    if ($seq = /^>.*$pattern/ ... (/^>/ && !/^>.*$pattern/)) {
      print unless $seq =~ /E0$/;
    }
  }
}


__DATA__
> alpha
apple
airplane
art
> beta
bear
blue
beat
> charlie
cow
cent
coat

输出:

> beta
bear
blue
beat

答案 4 :(得分:0)

如果匹配后总是相同的行数(三个)那么这个hackish“匹配倒计时”成语会起作用:

 perl -ne '$m=4 if (/^>s/ ); print if ($m-->0);'

我们使用"4"来匹配该行加上其后的行数(3),然后在我们前往$m--时使用print进行倒计时。我使用$m来模仿shell grep中的类似选项。但是,我实际上不确定这个一个成语,或者如果是这样的话,它的接受名称是什么。可能flip-flop范围...方法在脚本中更可取或更广泛使用 - 但这里是一个自包含的脚本,可以尝试使用更具有hackish的方法。

传入行数和模式作为参数进行搜索:

#!perl
use v5.16; # strict & warnings

my $linesafter ; 
my $num = shift ;                                                              
my $pat = shift ;                                                              

while (<DATA>) {                                                               
  $linesafter = $num if ( /^> $pat/ );                                         
  print if ($linesafter-->0); 
}                                                                              

__DATA__                                                                       
> alpha                                                                        
apple                                                                         
airplane                                                                     
art                                                                           
> beta                                                                         
bear                                                                           
blue                                                                          
beat                                                                          
> charlie                                                                     
cow                                                                           
cent                                                                        
coat