perl - 如何在文件中搜索包含 Perl 中关键字的最后一个连续行块
问题描述
想象一个像下面这样的文本文件,其中 <some random text> 可以是 any 或nothing,这意味着 KEYWORD 可以单独或与其他文本一起出现在行中的任何位置:
1 <some random text>
2 <some random text>KEYWORD<some random text>
3 <some random text>KEYWORD<some random text>
4 <some random text>
5 <some random text>
6 <some random text>KEYWORD<some random text>
7 <some random text>
8 <some random text>KEYWORD<some random text>
9 <some random text>KEYWORD<some random text>
10 <some random text>KEYWORD<some random text>
11 <some random text>
12 <some random text>KEYWORD<some random text>
13 <some random text>KEYWORD<some random text>
14 <some random text>
15 <some random text>KEYWORD<some random text>
16 <some random text>
如何获得包含关键字的2 个或更多连续行的最后一次出现(示例中的第 12 和 13 行)?需要明确的是,我对第 (8, 9, 10) 行不感兴趣,因为尽管它们包含关键字并且是连续的,但它们不是最后一行,也不是第 15 行,因为尽管它包含关键字并且是关键字的最后一行,它不是 2 个或更多连续行的一部分。
解决方案
记录这些带有图案的线条序列,始终保留最后一组,一旦文件出来,您将拥有最后一组。
直截了当的方法
use warnings;
use strict;
use feature 'say';
die "Usage: $0 file(s)\n" if not @ARGV;
my $threshold = 2;
my (@buf, $cnt, @res);
while (<>) {
if (not /KEYWORD/) {
$cnt = 0 if $cnt;
@buf = () if @buf;
next
}
++$cnt;
push @buf, $_;
if ($cnt >= $threshold) {
@res = @buf; # excessive copying; refine if a problem
}
}
print for @res;
(删除@ARGV
检查以允许STDIN
输入,它<>
读取时没有给出文件。)
笔记
行进入缓冲区,直到满足阈值条件(重复行数),并且计数器增加。在没有图案的线上,这些被重置
这里只有一次(只需要两条重复的行),因此以后处理将行复制到标量以保存它会更容易,但使用数组适用于任何阈值
一旦满足条件,缓冲区就会被复制。虽然需要对匹配阈值的第一行执行此操作,但要覆盖
@res
之前的行,对于以下重复行不需要复制整个数组 - 可以在阈值通过后添加行。这需要额外的小踢踏舞;这是一种方法(经过最低限度的测试)
while (<>) { if (not /KEYWORD/) { $cnt = 0 if $cnt; @buf = () if @buf; next } ++$cnt; if ($cnt < $threshold) { push @buf, $_; } elsif ($cnt == $threshold) { @res = (@buf, $_); } else { push @res, $_ } }
现在,当具有模式的行添加到大于阈值的计数时,第一次复制缓冲区,但添加以下行时没有额外的缓冲区副本。(如果这样的行序列非常少,或者文件很小,这不会产生明显的影响。)
如果您需要知道文件中的哪些位置保存行号$.
以及行。
如果一个文件可能很大——这是唯一要做的事情——我们可以使用相同的代码,但从文件末尾向后退。一个模块是File::ReadBackwards。
为了说明增益,这里有一个程序通过向后读取文件来做同样的事情
use warnings;
use strict;
use feature 'say';
use File::ReadBackwards;
my (@buf, $cnt, @res);
my $threshold = 2;
my $bw = File::ReadBackwards->new(shift) or die $!;
#print $bw->readline until $bw->eof; exit; # test
while ( my $line = $bw->readline ) {
if (not $line =~ /KEYWORD/) {
last if @res >= $threshold;
$cnt = 0 if $cnt;
@buf = () if @buf;
next
}
++$cnt;
if ($cnt < $threshold) {
push @buf, $line;
}
elsif ($cnt == $threshold) {
@res = (@buf, $line);
}
else {
push @res, $line;
}
}
print for reverse @res;
这会产生与从头开始读取的程序相同的输出。
我将测试文件附加了 200k 次,文件大小为 111 Mb。第一个程序(在注释中进行了调整)使用 ~ 1.85 sec
(几次运行的平均值),而上面的程序进入0.02 sec
. †</sup>
因此,对于足够大的文件,节省是很不错的;从后面阅读的小开销是完全看不到的。但是,在此过程中无法进行其他处理。此外,目标必须是可搜索的(文件),并且支持的操作很少;一方面,我们没有得到行号。
†</sup> 这适用于整个程序、启动和所有程序,time
在调用程序时在命令行上测量,并在几次运行中取平均值。
当我使用Time::HiRes仅对代码本身计时时,处理文件的运行时是
例如,在第四个 (4th) 小数位的第二个程序中
0.0003 sec
在第一个程序中,它当然还是
1.8881 sec
这样的
推荐阅读
- node.js - 如何将值从 React 文件传递给变量到节点服务器文件
- c# - 我正在尝试查找字符串 s1 是否为回文,反转字符串并将其保存在 s2 中,现在 s1==s2 ,在这里我永远不会正确
- java - 发送批量通知,想决定调度器的时间频率
- azure - 用于部署事件网格的 Azure devops 管道,其中包含来自 2 个不同 azure 订阅的资源
- javascript - 在 jQuery 数据表中看不到 PDF 按钮
- python - 为什么我的 python tkinter 图形在连接到 mysql 数据库时不能在 pycharm 之外运行
- java - 如何在redis缓存java中存储非持久java对象
- flutter - 如何在 FLUTTER 中设置一个按钮以重定向到我的地图?
- android - 如何在android studio中为意图创建公共类方法
- angular - 非相关组件之间使用 EventEmitter 进行通信