使用 Perl 打印多行

Using Perl to print multiple lines

此代码从我拥有的文本文件中获取关键字 'fun',然后打印关键字前后的 20 个字符。但是,我还希望它打印前两行和后两行,但我不确定该怎么做。我不确定用这个更改代码或一次读取整个文件是否更容易。

{my $inputfile = "file";
$searchword = 'fun';
open (INPUT, '<', $inputfile)  or die "fatal error reading the file \n";
while ($line1=<INPUT>)
{  
#read in a line of the file
 if ($line1 =~m/$searchword/i)
 {print "searchword found\n";
  $keepline = $line1;
    $goodline =1;

    $keepline =~/(.{1,20})(fun)(.{1,20})/gi;

    if ($goodline==1)
    {&write_excel};
 $goodline =0;                
 }

你的代码似乎是

  1. 'pledge'的每一边取20个字符,而不是$searchword
  2. 开头有一个不匹配的'{';
  3. 不打印除我们无法检查的 &write_excel 的任何文件内容;和
  4. 有逻辑问题,如果找到$searchword$goodline无条件设置为'1',然后测试是否为'1',最后重置为'0'

撇开这个不谈,关于是否读入整个文件的问题取决于你的情况——你要搜索的文件有多大,你的机器有足够的内存;机器是共享资源等等。我假设你可以阅读整个文件,因为这是我经验中更常见的立场(那些不同意的人请记住(a)我承认它有争议;(b)它非常依赖于只有 OP 知道的情况)

鉴于此,有多种方法可以读取整个文件,但 consensus seems to be 与模块 File::Slurp 一起使用。给定这些参数,答案如下所示;

#!/usr/bin/env perl
use v5.12;
use File::Slurp;

my $searchword = 'fun';
my $inputfile  = "file.txt";
my $contents   = read_file($inputfile);

my $line = '\N*\n';
if ( $contents =~ /(
       $line?
       $line?
       \N* $searchword \N* \n?
       $line?
       $line?
   )/x) {
  say "Found:\n" .  ;
}
else {
  say "Not found."
}
如果文件不存在(或其他问题),

File::Slurp 会打印一条合理的错误消息,因此我省略了典型的 or die...。每当使用正则表达式时——尤其是当你试图匹配多行内容时,使用 "extended mode"(通过在最后一个“/”之后放置一个 'x')来允许 insignificant 正则表达式中的空格。这样可以使布局更清晰。

为了更加清晰,我还分离了一行的定义,它由 0 个、1 个或多个非换行符组成,\N*,后跟一个新行,\n。但是,如果您的目标位于第一行、第二行、倒数第二行或最后一行,我认为您仍然需要该信息,因此可以选择匹配所请求的前后两行。 $line?

请注意,正则表达式是迂腐的,不可避免地 'fine details' 会影响成功匹配与不需要的匹配的定义 - 即。不要指望这 完全 在所有情况下都能满足您的要求。预计您将不得不进行一些试验和调整。

我不确定我是否理解您的代码块("pledge" 有什么用途?&write_excel 是什么?),但我可以自己回答您的问题。

首先,这个grep命令可以接受吗?它更快更干净:

grep -i -C2 --color "fun" "file"

-C NUM 标志告诉 grep 为每个模式匹配提供 NUM 行上下文。显然,--color 是可选的,但它可以帮助您在很长的行中找到匹配项。

否则,这里有一点 perl:

#!/usr/bin/perl

my $searchword = "fun";
my $inputfile = "file";

my $blue = "\e[1;34m";    # change output color to blue
my $green = "\e[1;32m";   # change output color to green
my $nocolor = "\e[0;0m";  # reset output to no color

my $prev1 = my $prev2 = my $result = "";

open (INPUT, '<', $inputfile) or die "fatal error reading the file \n";
while(<INPUT>) {
  if (/$searchword/i) {
    $result .= $prev2 . $prev1 . $_;  # pick up last two lines
    $prev2 = $prev1 = "";             # prevent reusing last two lines
    for (1..2) {                      # for two more non-matching lines
      while (<INPUT>) {               # parse them to ensure they don't match
        $result .= $_;                # pick up this line
        last unless /$searchword/i;   # reset counting if it matched
      }
    }
  } else {
    $prev2 = $prev1;                  # save last line as $prev2
    $prev1 = $_;                      # save current line as $prev1
  }
}
close $inputfile;

exit 1 unless $result;                # return with failure if without matches

$result =~                            # add colors (okay to remove this line)
  s/([^\e]{0,20})($searchword)([^\e]{0,20})/$blue$green$blue$nocolor/g;
print "$result";                      # print the result
print "\n" unless $result =~ /\n\Z/m; # add newline if there wasn't already one

错误:这假设前后两行实际上是 20+ 个字符。如果您需要修复此问题,请将其放入 else 节。