Как извлечь строки между двумя разделителями строк в 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 строка или другое элементы документации, которые несколько связаны с тем, что я ищу.

6 ответов


вы хотите оператор триггера (более известный как оператор диапазона) ..

#!/usr/bin/env perl
use strict;
use warnings;

while (<>) {
  if (/START/../END/) {
    next if /START/ || /END/;
    print;
  }
}

замените вызов print С тем, что вы действительно хотите сделать (например, вставьте линию в массив, отредактируйте ее, отформатируйте ее, что угодно). Я next-ING мимо строк, которые на самом деле имеют START или END, но вы можете не хотеть такого поведения. См.в этой статье для обсуждения этого оператора и других полезных специальных переменных Perl.


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


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

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

Если бы вы хотели текст, а не строки, вы бы использовали

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

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

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

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

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

Как это? В этом случае конечная строка равна$^, вы можете изменить ее на свою конечную строку.

Я также новичок, но решения там предоставляют довольно много методов... дайте мне знать более конкретно, что вы хотите, что отличается от приведенной выше ссылки.


while (<>) {
    chomp;      # strip record separator
    if(/END/) { $f=0;}
    if (/START/) {
        s/.*START//g;
        $f=1;
    }
    print $_ ."\n" if $f;
}

попробуйте написать код в следующий раз вокруг


после ответа Телемаха началось излияние. В конце концов, это работает как решение, на которое я смотрю.

  1. Я пытаюсь извлечь строки, разделенные двумя строками (одна строка заканчивается на "CINFILE="; другая строка содержит одну "#") в отдельных строках, исключая строки разделителя. Это я могу сделать с помощью решения Телемаха.
  2. в первой строке есть пробел, который я хочу удалить. Я также включаю его.
  3. Я также пытаюсь извлечь каждая строка-набор в отдельные файлы.

это работает для меня, хотя код можно классифицировать как уродливый; это потому, что я в настоящее время практически новичок в 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; } }
}

Я надеюсь, что это принесет пользу и другим. Овации.


Не так уж плохо, что пришли от "виртуальной newcommer". Одна вещь, которую вы могли бы сделать, - это поместить "$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                                                           

}