Использование Perl для печати нескольких строк

Этот код извлекает ключевое слово fun из имеющихся у меня текстовых файлов, а затем печатает 20 символов до и после ключевого слова. Тем не менее, я также хочу напечатать предыдущие 2 строки и следующие две строки, и я не уверен, как это сделать. Я не был уверен, проще ли изменить код с этим или просто прочитать весь файл за один раз.

{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;                
 }

2 ответа

Решение

Ваш код как есть кажется

  1. Не берите по 20 символов с каждой стороны $searchword;
  2. Имейте непревзойденный '{' в начале;
  3. Не печатает содержимое файла, кроме & write_excel, которое мы не можем исследовать; а также
  4. Есть логическая проблема в том, что если $searchwordнайден,$goodlineбезусловно устанавливается на "1", а затем проверяется, чтобы увидеть, является ли его "1" и, наконец, сбрасывается на "0"

Если оставить это в стороне, вопрос о том, нужно ли читать весь файл, зависит от ваших обстоятельств, а также от того, насколько велики файлы, которые вы собираетесь искать, у вашей машины достаточно памяти; машина является общим ресурсом и так далее. Я собираюсь предположить, вы можете прочитать в весь файл, как это более общая позиция в моем опыте (те, кто не согласен, пожалуйста, имейте в виду (а) Я квитирования, что его спорна, и (б) его очень сильно зависит от обстоятельства, которые знает только ОП)

Принимая во внимание, что есть несколько способов чтения целого файла, но консенсус, похоже, заключается в использовании модуля 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" . $1 ;
}
else {
  say "Not found."
}

File::Slurpвыводит разумное сообщение об ошибке, если файл отсутствует (или что-то еще идет не так), поэтому я пропустил типичныйor die..., Всякий раз, когда вы работаете с регулярными выражениями - особенно если вы пытаетесь сопоставить материал в нескольких строках, стоит использовать "расширенный режим" (помещая "x" после последнего "/"), чтобы разрешитьнезначительные пробелы в регулярном выражении. Это позволяет более четкое расположение.

Я также выделил определение линии для большей ясности, которая состоит из 0, 1 или более не-новых символов,\N*, а затем новая строка,\n, Однако, если ваша цель находится на первой, второй, второй-последней или последней строке, я предполагаю, что вы все еще хотите получить информацию, поэтому запрашиваемые предшествующая и последующая пары строк могут быть опционально сопоставлены. $line?

Обратите внимание, что регулярные выражения педантичны, и неизбежно возникают "мелкие детали", которые влияют на определение успешного сопоставления с нежелательным совпадением, т.е. Не ожидайте, что это сделаетименно то , что вы хотите при любых обстоятельствах. Ожидайте, что вам придется экспериментировать и немного подправлять.

Я не уверен, что понимаю ваш блок кода (для чего нужен "залог"? &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$1$green$2$blue$3$nocolor/g;
print "$result";                      # print the result
print "\n" unless $result =~ /\n\Z/m; # add newline if there wasn't already one

Ошибка: это предполагает, что две строки до и две строки после на самом деле более 20 символов. Если вам нужно исправить это, он идет в else строфа.

Другие вопросы по тегам