我想在输入文件中将@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/);
}
}
答案 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