Как я могу захватить несколько строк после соответствующей строки в Perl?

Я разбираю большой файл в Perl по очереди (завершается\n), но когда я достигаю определенного ключевого слова, скажу "TARGET", мне нужно захватить все линии между TARGET и следующим полностью пустая строка.

Итак, заданный сегмент файла:

Линия 1
Строка 2
Строка 3
Целевая задача 4 Строка 5 Возьмите эту строку
Линия 6 Возьмите эту строку
\ П

Это должно стать:
Целевая задача 4 Строка 5 Возьмите эту строку
Строка 6 Возьмите эту строку

Причина, по которой у меня возникают проблемы, я уже просматриваю файл по очереди; как изменить то, что я разделил на полпути через процесс синтаксического анализа?

Ответ 1

Вы хотите что-то вроде этого:

my @grabbed;
while (<FILE>) {
    if (/TARGET/) {
        push @grabbed, $_;
        while (<FILE>) {
            last if /^$/;
            push @grabbed, $_;
        }
    }
}

Ответ 2

Оператор диапазона идеален для такого рода задач:

$ cat try
#! /usr/bin/perl

while (<DATA>) {
  print if /\btarget\b/i .. /^\s*$/
}

__DATA__
Line 1
Line 2
Line 3
Line 4 Target
Line 5 Grab this line
Line 6 Grab this line

Nope
Line 7 Target
Linu 8 Yep

Nope again

$ ./try
Line 4 Target
Line 5 Grab this line
Line 6 Grab this line

Line 7 Target
Linu 8 Yep

Ответ 3

Короткий ответ: разделитель строк в perl $/, поэтому, когда вы нажимаете TARGET, вы можете установить $/ в "\n\n", прочитать следующую строку, а затем установить его на "\n"... et voilà!

Теперь для более длинного: если вы используете модуль English (который дает разумные имена для всей магической переменной Perl, то $/ называется $RS или $INPUT_RECORD_SEPARATOR. Если вы используете IO::Handle, то IO::Handle->input_record_separator( "\n\n") будет работать.

И если вы делаете это как часть большого фрагмента кода, не забудьте либо локализовать (используя local $/; в соответствующей области), либо установить $/ на его исходное значение "\n".

Ответ 4

От perlfaq6 ответ на Как я могу вытащить линии между двумя шаблонами, которые сами находятся на разных линиях?


Вы можете использовать Perl несколько экзотический.. оператор (задокументированный perlop):

perl -ne 'print if /START/ .. /END/' file1 file2 ...

Если вам нужен текст, а не строки, вы должны использовать

perl -0777 -ne 'print "$1\n" while /START(.*?)END/gs' file1 file2 ...

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

Вот еще один пример использования..:

while (<>) {
    $in_header =   1  .. /^$/;
    $in_body   = /^$/ .. eof;
# now choose between them
} continue {
    $. = 0 if eof;  # fix $.
}

Ответ 5

while(<FILE>)
{
    if (/target/i)
    {
        $buffer .= $_;
        while(<FILE>)
        {
            $buffer .= $_;
            last if /^\n$/;
        }
    }
}

Ответ 6

use strict;
use warnings;

my $inside = 0;
my $data = '';
while (<DATA>) {
    $inside = 1 if /Target/;
    last if /^$/ and $inside;
    $data .= $_ if $inside;
}

print '[' . $data . ']';

__DATA__
Line 1
Line 2
Line 3
Line 4 Target
Line 5 Grab this line
Line 6 Grab this line

Next Line

Изменить, чтобы исправить условие выхода в соответствии с примечанием ниже.

Ответ 7

Если вы не против уродливого автоматически сгенерированного кода и предполагаете, что вам нужны строки между TARGET и следующей пустой строкой и хотите, чтобы все остальные строки были удалены, вы можете использовать вывод этой команды:

s2p -ne '/TARGET/,/^$/p'

(Да, это намек на то, что эту проблему, как правило, гораздо легче решить в sed.: -P)

Ответ 8

Если вам нужен только один цикл (изменение кода Дэйва Хинтона):

my @grabbed;
my $grabbing = 0;
while (<FILE>) {
    if (/TARGET/ ) {
       $grabbing = 1;
    } elsif( /^$/ ) {
       $grabbing = 0;
    }
    if ($grabbing) {
        push @grabbed, @_;
    }
}

Ответ 9

while (<IN>) {
print OUT if (/Target/../^$/) ; 
}