在perl中保留前一行和/或后续行的信息的最佳/正确方法是什么。例如,使用以下代码:
while (<IN>) {
print;
}
只有当文件中的上一行或下一行与foo匹配时才能更改为不打印行,但是否则打印?
你能给出代码示例吗?感谢。
答案 0 :(得分:2)
基本上,如果要根据其他两行中包含的信息打印当前行,则需要跟踪两条额外的行。这是一个简单的脚本,所有内容都是硬编码的:
#!/usr/bin/env perl
use strict;
use warnings;
my $prev = undef;
my $candidate = scalar <DATA>;
while (defined $candidate) {
my $next = <DATA>;
unless (
(defined($prev) && ($prev =~ /foo/)) ||
(defined($next) && ($next =~ /foo/))
) {
print $candidate;
}
($prev, $candidate) = ($candidate, $next);
}
__DATA__
1
2
foo
3
4
5
foo
6
foo
7
8
9
foo
我们可以将它概括为一个带文件句柄和测试的函数(作为子例程引用):
#!/usr/bin/env perl
use strict; use warnings;
print_mid_if(\*DATA, sub{ return !(
(defined($_[0]) && ($_[0] =~ /foo/)) ||
(defined($_[1]) && ($_[1] =~ /foo/))
)} );
sub print_mid_if {
my $fh = shift;
my $test = shift;
my $prev = undef;
my $candidate = scalar <$fh>;
while (defined $candidate) {
my $next = <$fh>;
print $candidate if $test->($prev, $next);
($prev, $candidate) = ($candidate, $next);
}
}
__DATA__
1
2
foo
3
4
5
foo
6
foo
7
8
9
foo
答案 1 :(得分:2)
你可以将你的行读入一个数组,然后如果你得到一些以某种方式发出信号的东西,则弹出数组的最后几个元素。一旦你完成了所有内容的阅读,就可以打印出来了:
use strict;
use warnings;
use feature qw(say);
use autodie; #Won't catch attempt to read from an empty file
use constant FILE_NAME => "some_name.txt"
or die qq(Cannot open ) . FILE_NAME . qq(for reading: $!\n);
open my $fh, "<", FILE_NAME;
my @output;
LINE:
while ( my $line = <DATA> ) {
chomp $line;
if ( $line eq "foo" ) {
pop @output; #The line before foo
<DATA>; #The line after foo
next LINE; #Skip line foo. Don't push it into the array
}
push @output, $line;
}
从那里,您可以使用您不想打印的值打印出数组。
for my $line ( @output ) {
say $line;
}
唯一的问题是这需要记忆。如果您的文件非常大,则可能内存不足。
解决此问题的一种方法是使用缓冲区。将值存储在数组中,并在数组中按另一个值时移出最后一个值。如果读入的值为foo
,则可以重置阵列。在这种情况下,缓冲区最多只包含一行:
#! /usr/bin/env perl
use strict;
use warnings;
use autodie;
use feature qw(say);
my @buffer;
LINE:
while ( my $line = <DATA> ) {
chomp $line;
if ( $line eq "foo" ) {
@buffer = (); #Empty buffer of previous line
<DATA>; #Get rid of the next line
next LINE; #Foo doesn't get pushed into the buffer
}
push @buffer, $line;
if ( @buffer > 1 ) { #Buffer is "full"
say shift @buffer; #Print out previous line
}
}
#
# Empty out buffer
#
for my $line ( @buffer ) {
say $line;
}
__DATA__
2
3
4
5
6
7
8
9
10
11
12
13
1
2
foo
3
4
5
foo
6
7
8
9
foo
请注意,当我跳过下一行时,我可能会尝试从空文件中读取数据。这没关系。 <$fh>
将返回空字符串或undef,但我可以忽略它。当我回到循环的顶部时,我会发现错误。
答案 2 :(得分:2)
我没有看到你对“最佳”有任何具体标准,所以我会给你一个解决方案,该解决方案沿着与目前为止所呈现的不同的轴线“最佳”。您可以使用Tie::File并将整个文件视为数组,然后使用索引迭代数组。上一行和下一行分别只是$index-1
和$index+1
。你只需要担心你的索引超出了数组的范围。这是一个例子:
#!/usr/bin/env perl
use strict;
use warnings;
use 5.010; # just for "say"
use Tie::File;
tie my @array, 'Tie::File', "filename" or die;
for my $i (0..$#array) {
if ($i > 0 && $i < $#array) { # ensure $i-1 and $i+1 make sense
next if $array[$i-1] =~ /BEFORE/ &&
$array[$i+1] =~ /AFTER/;
}
say $array[$i];
}
如果更方便,您可以指定文件句柄而不是文件名,Tie::File也有一些参数来控制内存使用情况,或者如果您愿意,可以更改“行”的含义。查看文档以获取更多信息。
无论如何,如果您熟悉数组并且喜欢用数组来思考,那么这是另一种做你想要的方法,这可能在概念上更简单。
答案 3 :(得分:1)
我会将文件读入一个数组,每行都是一个数组元素,然后你可以进行比较。唯一真正的设计考虑因素是正在读入内存的文件大小。