gpt4 book ai didi

perl - 如何在文件中搜索包含 Perl 中关键字的最后一个连续行 block

转载 作者:行者123 更新时间:2023-12-03 23:34:22 26 4
gpt4 key购买 nike

想象一个像下面这样的文本文件,其中 可以是任何东西或 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, $_
    }
    }

    现在第一次复制缓冲区时,带有模式的行添加到计数大于阈值,但添加以下行时没有额外的缓冲区复制。 (如果这样的行序列很少,或者文件很小,这不会产生明显的影响。)

如果您需要知道文件中的位置,这些文件将保存在 line number $. 中。 , 以及线条。

如果一个文件可能很大——这是唯一要做的事情——我们可以使用相同的代码,但从文件末尾向后。一个模块是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 .

因此,对于足够大的文件,节省是很不错的;小开销in reading from the back是完全看不见的。但是,在此过程中无法进行其他处理。此外,目标必须是可搜索的(文件),并且支持的操作很少;一方面,我们没有得到行号。


这适用于整个程序、启动和所有,由time测量在调用程序时在命令行上显示,并在几次运行中取平均值。

当我只计时代码本身时,使用 Time::HiRes ,处理文件的运行时是

  • 在第二个程序中第四(4)位小数,例如0.0003 sec

  • 在第一个程序中当然还是 1.8881 sec或者一些这样的

关于perl - 如何在文件中搜索包含 Perl 中关键字的最后一个连续行 block ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62565949/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com