Как извлечь строки между двумя разделителями строк в 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                                                           

}