如何在两种模式之间打印线条?

时间:2016-06-21 03:54:59

标签: regex string perl

我想在输入文件中将@cluster t.# has ### elements行(包括此行)和@cluster t.#+1 has ### elements(最好省略此行)之间的所有内容打印到相应的编号输出文件(clust(#).txt)中。到目前为止,该脚本创建了适当的编号文件,没有任何内容。

#!/usr/bin/perl 

use strict;
use warnings;

open(IN,$ARGV[0]);

our $num = 0;

while(my $line = <IN>) {
    if ($line =~ /^\@cluster t has (\d+) elements/) {
        my $clust = "full";
        open (OUT, ">clust$clust.txt");

    } elsif ($line =~ m/^\@cluster t.(\d+.*) has (\d+) elements/) {
        my $clust = $1;
        $num++;
        open (OUT, ">clust$clust.txt");
        print OUT, $_ if (/$line/ ... /$line/);
    }
}

2 个答案:

答案 0 :(得分:7)

更新重新安排,以便基于我对输入的最终理解的版本首先出现。为清晰起见,也进行了编辑。

检测启动要写入其自己文件的部分的行并打开合适的文件;否则只需写入文件句柄(对应于当前输出文件)。

示例输入文件,据我所知data_range.txt

@cluster t.1 has 100 elements
data 1
data 1 1
@cluster t.2 has 200 elements
data 2
@cluster t.3 has 300 elements

t.N以及后续t.N后面的行打印到文件clust(N).txt

use warnings;
use strict;

my $file = shift || 'data_range.txt';
open my $fh, $file  or die "Can't open $file: $!";

my $fh_out;

my $clustline = qr/\@cluster t\.([0-9]+) has [0-9]+ elements/;

while (<$fh>) 
{
    if (/$clustline/) {
        my $outfile = "clust($1).txt";
        open $fh_out, '>', $outfile or die "Can't open $outfile: $!";
    }

    print $fh_out $_;
}

对于@cluster的每一行,打开一个带有相应编号的新文件,关闭前一个文件,因为我们使用相同的文件句柄。所有后续行(包括那一行)都属于该文件,并在那里打印。

上面的代码假定文件中的第一行是@cluster行,并且此文件中的所有行都属于一个输出文件。如果不是这样,那么我们需要更加小心:(1)在写入开始时使用标志;(2)添加允许跳过行的分支。

my $started_writing = 0;

my $clustline = qr/\@cluster t\.([0-9]+) has [0-9]+ elements/;

while (<$fh>) 
{
    if (/$clustline/) {
        my $fout = "clust($1).txt";
        open $fh_out, '>', $fout or die "Can't open $fout for writing: $!";
        $started_writing = 1;
    }
    elsif (not $started_writing) {   # didn't get to open output files yet
        next;
    }
    elsif (/dont_write_this_line/) { # condition for lines to skip altogether
        next;
    }

    print $fh_out $_;
}

所有这些都假设@cluster行不能以相同的数字重复。如果发生这种情况,您将丢失输出数据,因此如果您不确定输入(或在附加模式下打开输出文件),请添加测试。

我们得到输出clust(1).txt

@cluster t.1 has 100 elements
data 1
data 1 1

clust(2).txt

@cluster t.2 has 200 elements
data 2
带有clust(3).txt行的

@cluster t.3

原始版本,初步了解输入和要求

range operator几乎是为此量身定做的。它通过重复呼叫跟踪其真/假状态。一旦它的左操作数评估为真并且保持这种方式直到右边的操作数为真,后它为假,那么它将变为真,所以在下一次评估时。还有更多内容,请参阅文档。

虚拟输入文件data_range.txt

@cluster t.1 has 100 elements
@cluster t.2 has 200 elements
@cluster t.3 has 300 elements
@cluster t.4 has 400 elements
@cluster t.5 has 500 elements

在标记线2和4之间打印所有内容,包括起始行,但不包括结束行。

use warnings;
use strict;

my $file = 'data_range.txt';
open my $fh, $file  or die "Can't open $file: $!";

# Build the start and end patterns
my $beg = qr/^\@cluster t\.2 has 200 elements$/;
my $end = qr/^\@cluster t\.4 has 400 elements$/;

while (<$fh>) 
{
    if (/$beg/ .. /$end/) {
        print if not /$end/;
    }   
}

这会打印第2行和第3行。..运算符会在行($_)与$beg匹配时变为true,并且在行匹配$end之前为true。在那之后它是错误的,对于下一行。因此它最终也包括起始和结束行。所以我们也测试结束标记,如果我们有那条线就不打印。

如果您更愿意使用文字标记线,则可以测试字符串是否相等

my $beg = q(@cluster t.2 has 200 elements);
my $end = q(@cluster t.4 has 400 elements);

while (my $line = <$fh>) 
{
    chomp($line);
    if ($line eq $beg .. $line eq $end) {
        print "$line\n" if $line ne $end;
    }   
}

这与上面的示例相同。请注意,现在我们必须chomp,因为新行会阻止eq测试(然后我们会添加\n进行打印)。

答案 1 :(得分:2)

我通过搜索引擎搜索了这个问题,我有一个简明的回复提供:

伪代码

perl -ne 'print if /begin_string/ .. /ending_string/' file.txt

真实代码

perl -ne 'print if /^foo/ .. /^base/' file.txt

示例输入

Lorem ipsum dolor
sit amet,
consectetur adipiscing
foo
bar
base
elit,
sed do

输出

foo
bar
base