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

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

Итак, учитывая сегмент файла:

Строка 1
Линия 2
Линия 3
Линия 4 Цель
Строка 5 Захватите эту строку
Строка 6 Захватите эту строку
\ п

Это должно стать:
Линия 4 Цель
Строка 5 Захватите эту строку
Строка 6 Захватите эту строку

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

Ответы на вопрос(9)

Решение Вопроса

Ты хочешь что-то вроде этого:

my @grabbed;
while (<FILE>) {
    if (/TARGET/) {
        push @grabbed, $_;
        while (<FILE>) {
            last if /^$/;
            push @grabbed, $_;
        }
    }
}
 ysth25 июн. 2009 г., 02:37
Если дескриптор указывает не на реальный файл, а на что-то вроде STDIN, вы можете получить внутреннее время, получить eof и завершиться, а затем внешнее, пока продолжить чтение доЭт получает eof. Попробуйте это с: perl -wle'print "read a"; while (<>) {print "read b"; while (<>) {print "read b"} print "read a"} '
 Dirk24 июн. 2009 г., 22:15
Ах, спасибо, я не был уверен, что в то время как <FILE> внутри другого, пока <FILE> был в порядке в perl:)
 Sinan Ünür24 июн. 2009 г., 22:22
@ Michael Это просто очередной вызов readline, так что да, все в порядке. perldoc -f readline

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

$ 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
 user10503325 июн. 2009 г., 20:45
benchmark это против первого решения

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

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

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

 Chris Jester-Young25 июн. 2009 г., 13:23
Спасибо за хедз-ап! Я редко возвращаюсь, чтобы проверить ответы других людей, поэтому хорошо, что дан явно более качественный ответ.
 user5540025 июн. 2009 г., 09:14
Смотри ответ Гбэкона. Это можно записать как "perl -ne 'print if / TARGET / .. / ^ $ /'", что более или менее точно соответствует тому, что у вас есть.
while(<FILE>)
{
    if (/target/i)
    {
        $buffer .= $_;
        while(<FILE>)
        {
            $buffer .= $_;
            last if /^\n$/;
        }
    }
}
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

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

 telesphore424 июн. 2009 г., 22:48
D0h! Я должен изменить это на "последний, если / ^ $ / и $ внутри;" обрабатывать случай, когда перед целью стоит пустая строка.
 Ape-inago24 июн. 2009 г., 22:36
Я был бы против флагов, но это один из самых ярких примеров, которые я когда-либо видел!

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

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

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

 mirod25 июн. 2009 г., 06:58
@ Крис Лутц, вы правы, я просто предположил, что если вы используете английский, то вы бы прочитали документы.
 Ape-inago24 июн. 2009 г., 22:37
Мне нравится, как ты объяснил способ сделать это без указания кода. Это немного дольше, но, в конце концов, читателю лучше сделать что-то подобное в будущем.
 Chris Lutz24 июн. 2009 г., 23:34
Если тыuse English; (что я не знаю, но то, что плавает на твоей лодке) обязательноuse English '-no_match_vars';, иначе вы получите снижение производительности с помощью регулярных выражений.

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 $.
}

my @grabbed;
my $grabbing = 0;
while (<FILE>) {
    if (/TARGET/ ) {
       $grabbing = 1;
    } elsif( /^$/ ) {
       $grabbing = 0;
    }
    if ($grabbing) {
        push @grabbed, @_;
    }
}
 Ape-inago24 июн. 2009 г., 22:35
посмотрите на некоторые другие примеры здесь ... следует избегать флагов $, поскольку это код 'perl', и поэтому вы должны использовать perl-isms.
 ysth25 июн. 2009 г., 02:41
Используйте флаги, если это то, что имеет для вас смысл. «Любой уровень владения языком приемлем в культуре Perl. Мы не будем отправлять языковой полиции после вас. Сценарий Perl является «правильным», если он выполняет свою работу до того, как ваш босс уволит вас ». - Ларри Уолл
 Dirk24 июн. 2009 г., 22:41
@ Обезьяна Вы можете объяснить? (Я только что заметил, что я использую «флаги» в коде в другом месте)
while (<IN>) {
print OUT if (/Target/../^$/) ; 
}   

Ваш ответ на вопрос