Как извлечь строки между двумя разделителями строк в Perl?
У меня есть файл журнала ASCII с некоторым содержимым, которое я хотел бы извлечь. Я никогда не занимался изучением Perl, но считаю, что это хороший инструмент для этой задачи.
Файл структурирован следующим образом:
...
... some garbage
...
... garbage START
what i want is
on different
lines
END
...
... more garbage ...
next one START
more stuff I want, again
spread
through
multiple lines
END
...
more garbage
Итак, я ищу способ извлечь строки между строками-разделителями START
и END
.
Как я могу это сделать?
До сих пор я нашел несколько примеров того, как печатать строку со строкой START
или другими элементами документации, которые несколько связаны с тем, что я ищу.
Ответы
Ответ 1
Вам нужен оператор триггера (более известный как оператор диапазона) ..
#!/usr/bin/env perl
use strict;
use warnings;
while (<>) {
if (/START/../END/) {
next if /START/ || /END/;
print;
}
}
Замените вызов на print
тем, что вы действительно хотите сделать (например, нажмите строку в массив, отредактируйте ее, отформатируйте, что угодно). Я next
, минуя строки, которые на самом деле имеют START
или END
, но вы можете не хотеть этого поведения. См. эту статью для обсуждения этого оператора и других полезных специальных переменных Perl.
Ответ 2
От 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 $.
}
Ответ 3
Как я могу захватить несколько строк после соответствующей строки в Perl?
Как это? В этом случае строка END равна $^, вы можете изменить ее на свою конечную строку.
Я тоже новичок, но в решениях есть немало способов... дайте мне знать более конкретно то, что вы хотите, которое отличается от приведенной выше ссылки.
Ответ 4
while (<>) {
chomp; # strip record separator
if(/END/) { $f=0;}
if (/START/) {
s/.*START//g;
$f=1;
}
print $_ ."\n" if $f;
}
попробуйте написать код в следующий раз
Ответ 5
После ответа Телемаха все началось. Это работает как решение, которое я ищу в конце концов.
- Я пытаюсь извлечь строки, разделенные двумя строками (одна с линией, заканчивающейся "CINFILE =", другая с линией, содержащей один "#" ) в отдельных строках, за исключением строк разделителя. Это я могу сделать с решением Telemachus.
- В первой строке есть пробел, который я хочу удалить. Я также включаю его.
- Я также пытаюсь извлечь каждый набор строк в отдельные файлы.
Это работает для меня, хотя код можно классифицировать как уродливый; это потому, что я в настоящее время практически новичок в Perl. В любом случае:
#!/usr/bin/env perl
use strict;
use warnings;
my $start='CINFILE=$';
my $stop='^#$';
my $filename;
my $output;
my $counter=1;
my $found=0;
while (<>) {
if (/$start/../$stop/) {
$filename=sprintf("boletim_%06d.log",$counter);
open($output,'>>'.$filename) or die $!;
next if /$start/ || /$stop/;
if($found == 0) { print $output (split(/ /))[1]; }
else { print $output $_; }
$found=1;
} else { if($found == 1) { close($output); $counter++; $found=0; } }
}
Я надеюсь, что это принесет пользу и другим.
Приветствия.
Ответ 6
Не так уж плохо для "виртуального новичка". Единственное, что вы могли бы сделать, это поместить "$ found = 1" внутри блока "if ($ found == 0)", чтобы вы не выполняли это задание каждый раз между $start и $stop.
Еще одна вещь, которая, на мой взгляд, немного уродлива, заключается в том, что вы открываете один и тот же файл-манипулятор каждый раз, когда вы вводите $start/$stop-block.
Это показывает путь вокруг этого:
#!/usr/bin/perl
use strict;
use warnings;
my $start='CINFILE=$';
my $stop='^#$';
my $filename;
my $output;
my $counter=1;
my $found=0;
while (<>) {
# Find block of lines to extract
if( /$start/../$stop/ ) {
# Start of block
if( /$start/ ) {
$filename=sprintf("boletim_%06d.log",$counter);
open($output,'>>'.$filename) or die $!;
}
# End of block
elsif ( /$end/ ) {
close($output);
$counter++;
$found = 0;
}
# Middle of block
else{
if($found == 0) {
print $output (split(/ /))[1];
$found=1;
}
else {
print $output $_;
}
}
}
# Find block of lines to extract
}