我正在解析格式如下的文本文件
> 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;
}
}
}
答案 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