Ответ 1
Вы хотите что-то вроде этого:
my @grabbed;
while (<FILE>) {
if (/TARGET/) {
push @grabbed, $_;
while (<FILE>) {
last if /^$/;
push @grabbed, $_;
}
}
}
Я разбираю большой файл в Perl по очереди (завершается\n), но когда я достигаю определенного ключевого слова, скажу "TARGET", мне нужно захватить все линии между TARGET и следующим полностью пустая строка.
Итак, заданный сегмент файла:
Линия 1
Строка 2
Строка 3
Целевая задача 4
Строка 5 Возьмите эту строку
Линия 6 Возьмите эту строку
\ П
Это должно стать:
Целевая задача 4
Строка 5 Возьмите эту строку
Строка 6 Возьмите эту строку
Причина, по которой у меня возникают проблемы, я уже просматриваю файл по очереди; как изменить то, что я разделил на полпути через процесс синтаксического анализа?
Вы хотите что-то вроде этого:
my @grabbed;
while (<FILE>) {
if (/TARGET/) {
push @grabbed, $_;
while (<FILE>) {
last if /^$/;
push @grabbed, $_;
}
}
}
Оператор диапазона идеален для такого рода задач:
$ 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
Короткий ответ: разделитель строк в perl $/
, поэтому, когда вы нажимаете TARGET, вы можете установить $/
в "\n\n"
, прочитать следующую строку, а затем установить его на "\n"... et voilà!
Теперь для более длинного: если вы используете модуль English
(который дает разумные имена для всей магической переменной Perl, то $/
называется $RS
или $INPUT_RECORD_SEPARATOR
. Если вы используете IO::Handle
, то IO::Handle->input_record_separator( "\n\n")
будет работать.
И если вы делаете это как часть большого фрагмента кода, не забудьте либо локализовать (используя local $/;
в соответствующей области), либо установить $/
на его исходное значение "\n"
.
От 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 $.
}
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
Изменить, чтобы исправить условие выхода в соответствии с примечанием ниже.
Если вы не против уродливого автоматически сгенерированного кода и предполагаете, что вам нужны строки между TARGET
и следующей пустой строкой и хотите, чтобы все остальные строки были удалены, вы можете использовать вывод этой команды:
s2p -ne '/TARGET/,/^$/p'
(Да, это намек на то, что эту проблему, как правило, гораздо легче решить в sed
.: -P)
Если вам нужен только один цикл (изменение кода Дэйва Хинтона):
my @grabbed;
my $grabbing = 0;
while (<FILE>) {
if (/TARGET/ ) {
$grabbing = 1;
} elsif( /^$/ ) {
$grabbing = 0;
}
if ($grabbing) {
push @grabbed, @_;
}
}
while (<IN>) {
print OUT if (/Target/../^$/) ;
}