Как извлечь определенное количество строк после шаблона в perl? - PullRequest
4 голосов
/ 19 февраля 2020

Допустим, у меня есть текстовый файл:

Line 1
Line 2
Target Name1
Line 3
Line 4
Line 5
Line 6
Target Name2
Line 7
Line 8
Line 9
Line 10

Я хочу иметь возможность искать целевой шаблон (их может быть несколько в текстовом файле, как в примере выше), а затем распечатать определенное количество строк, которые следуют за ним. Скажем, 3 строки. Итак, я хотел бы получить вывод

Target Name1
Line 3
Line 4
Line 5

Target Name2
Line 7 
Line 8
Line 9

Пока все, что у меня есть, - это код, чтобы найти цель и распечатать ее:

use strict;
use warning;
open (my $INFILE, $input_file);
my $outfile = "output.txt";
open (my $OUTFILE, '>', $outfile);

my $name;

while (my $line = <$INFILE>) {
  if ($line =~ m#TARGET\s+(\S+)#){
    $name = $1;
    print $OUTFILE "Target $name\n";
  }
}

Я не уверен, как распечатать следующие 3 строки после цели. Примечание. Предположим, что цели всегда находятся дальше, чем 3 линии друг от друга. Я думаю, что мне нужен счетчик, верно?

Ответы [ 6 ]

5 голосов
/ 19 февраля 2020

Основным способом c является использование флага и счетчика, когда начинать и сколько печатать. Один из способов:

use warnings;
use strict;

my $how_many = 3;

my $to_print = 0;    
while (<>) { 
    $to_print = 1+$how_many  if /Target\s+\S+/;

    print if $to_print-- > 0;
}

При этом используется одна переменная для управления операцией, в которой задается количество строк для печати (плюс одна) при каждом обнаружении «триггера», а затем обратный отсчет при каждой печати. Он делает некоторые предположения.

Оператор <> читает строки из файлов, заданных в командной строке (или из STDIN), поэтому запускайте скрипт, передавая имена файлов в качестве аргументов, когда он прибег.

3 голосов
/ 19 февраля 2020

Пропускайте строки, пока не достигнете целевой строки, затем выведите эту строку Внутри l oop прочитайте желаемое количество строк и выведите их. Вот один из способов сделать это:

while( <> ) {
    next unless /\ATarget\s+\S+/;  # skip until you find the start line
    print;                         # output the current line

    print scalar <> for 1 .. 3;    # output the next lines
    print "\n";                    # separate groups
    }
1 голос
/ 19 февраля 2020

Еще один ответ:

perl -ne 'print if /^Target\b/ and $l=$. or $. <= 3+$l&&$l' sample.txt

Это использует, что специальная Perl переменная $. имеет текущий номер строки ввода.

Если вы используете Gnu / Linux или аналогичная операционная система, а затем команда grep с завершающим контекстом из 3 строк после совпадений также является альтернативой. Если вы можете иметь -- разделительные линии между матчами:

grep -A3 Target sample.txt
grep -A3 '^Target ' sample.txt
grep -A3 -P '^Target\b' sample.txt
1 голос
/ 19 февраля 2020

Другой вариант ранее размещенного фрагмента кода

use strict;
use warnings;

my $how_many = 3;
my $pattern = qr/Target Name\d/;

while(<DATA>) {
    next unless /$pattern/;
    print;
    print scalar <DATA> for 1..$how_many;
    print "\n";
}

__DATA__
Line 1
Line 2
Target Name1
Line 3
Line 4
Line 5
Line 6
Target Name2
Line 7
Line 8
Line 9
Line 10

Вывод

Target Name1
Line 3
Line 4
Line 5

Target Name2
Line 7
Line 8
Line 9
1 голос
/ 19 февраля 2020

То же решение в несколько ином коде

use strict;
use warnings;

my $how_many = 3;
my $pattern = qr/Target Name\d/;

while(<DATA>) {
    next unless /$pattern/;
    print;
    my $count = $how_many;
    while( $count-- > 0 ) { print scalar <DATA> }
    print "\n";
}

__DATA__
Line 1
Line 2
Target Name1
Line 3
Line 4
Line 5
Line 6
Target Name2
Line 7
Line 8
Line 9
Line 10

Вывод

Target Name1
Line 3
Line 4
Line 5

Target Name2
Line 7
Line 8
Line 9
0 голосов
/ 19 февраля 2020

Позже отредактируйте, прочитав немного больше документации:

perl -ne 'print if $s = /Target/ ... $s == 2; print "\n" if $s == 3' sample.txt

оператор диапазона:

~# perl -ne 'print if (($l = $.) && /Target/) .. $. == $l+2; print "\n" if  $. == $l+2' sample.txt
Target Name1
Line 3
Line 4

Target Name2
Line 7
Line 8

Примечание: Это всего лишь академическое c упражнение в виде ответа, я не рекомендую использовать это, это слишком громоздко. Я просто хотел посмотреть, можно ли это сделать, и когда я это сделаю, я просто опубликовал это, может быть, кому-то будет интересно. zdim ответом является путь к go.

Примечание: в соответствии с документацией документация .., даже если в скалярном контексте он не ведет себя по-другому, у него нет специального имени, он по-прежнему является оператором диапазона. Однако некоторые люди любят называть это триггером. Я всегда связывал это с линейным диапазоном, и это имя я использую.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...