如果文本块的长度相同(以字节或字符计),则可以计算所需S值在文件中的位置,然后进行
seek
操作,接着读取。否则,原则上需要逐行阅读以找到S值。
但是,如果只需要找到几个S值,可以估计所需位置并进行
seek操作,然后
read足够多的内容来捕获一个S值。然后分析所读内容以查看偏差有多大,然后再次进行
seek
操作或使用
<>
逐行读取以获取S值。
use warnings;
use strict;
use feature 'say';
use Fcntl qw(:seek);
my ($file, $s_target) = @ARGV;
die "Usage: $0 filename\n" if not $file or not -f $file;
$s_target //= 5;
open my $fh, '<', $file or die $!;
my $est_text_len = 1024;
my $jump_by = $est_text_len * $s_target;
my ($buff, $found);
seek $fh, $jump_by, SEEK_CUR;
while (1) {
my $rd = read $fh, $buff, $est_text_len;
warn "error reading: $!" if not defined $rd;
last if $rd == 0;
while ($buff =~ /S=([0-9]+)/g) {
my $s_val = $1;
if ($s_val == $s_target) {
say "--> Found S=$s_val at pos ", pos $buff, " in buffer";
seek $fh, - $est_text_len + pos($buff) + 1, SEEK_CUR;
while (<$fh>) {
last if /S=[0-9]+/;
print $_;
}
$found = 1;
last;
}
}
last if $found;
}
通过使用您的示例进行测试,进行了扩展和清理(将文本中的
S = n
更改为与条件相同!),并将
$est_text_len
和
$jump_by
设置为100和20。
这是一个草图。完整的实现需要就代码中概述的过度和不足寻求进行协商。如果文本块大小变化不大,它可以在两次寻找和读取之前获得所需的S值,然后使用
<>
进行读取或使用正则表达式,如示例中所示。
一些评论
上述的“分析”需要仔细进行。首先,一个缓冲区可能包含多个S值行。此外,注意如果缓冲区中没有S值,代码将继续读取。
一旦您靠近并位于$s_target前面,请通过<>>读取行以到达它。
读取可能不会得到所请求的那么多,因此您应该真正将其放在循环中。最近有相关的帖子。
为了提高效率,请从read更改为
sysread。在这种情况下,请使用
sysseek,并且不要与<>>混合使用(这是缓冲的)。
上面的代码假定要查找一个S值;请根据需要进行调整。它绝对假定S值已排序。
这显然比阅读行复杂得多,但如果有一个非常大的文件和只需要找到几个S值,它会运行得更快。如果有许多值,则可能不会有所帮助。
在问题中指出的
foreach (<$fh>)
会导致整个文件先被读取(以建立列表供
foreach
遍历),应改用
while (<$fh>)
。
如果文件没有改变(或者同一个文件需要被多次搜索),你可以先处理一次,建立S值精确位置的索引。感谢
Danny_ds的评论。
while
。如果不行,读取特定大小的MB到缓冲区中,并计算换行符数量以定位所需行。 - mpapec