Как извлечь строки между двумя разделителями строк в Perl? - PullRequest
11 голосов
/ 31 июля 2009

У меня есть файл журнала 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 ]

22 голосов
/ 31 июля 2009

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

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

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

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

5 голосов
/ 31 июля 2009

От ответа 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 $.
}
1 голос
/ 05 августа 2009

Не так уж плохо для "виртуального новичка". Единственное, что вы можете сделать, это поместить «$ found = 1» внутри блока «if ($ found == 0)», чтобы вы не делали это назначение каждый раз между $ start и $ stop.

Другая вещь, которая, на мой взгляд, несколько уродлива, заключается в том, что вы открываете один и тот же обработчик файлов каждый раз, когда вводите блок $ start / $ stop.

Это показывает путь к этому:

#!/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                                                           

}
1 голос
/ 31 июля 2009

После ответа Телемаха все начало выливаться. Это работает как решение, которое я смотрю в конце концов.

  1. Я пытаюсь извлечь строки, разделенные двумя строками (одна со строкой, заканчивающейся на "CINFILE ="; другая, со строкой, содержащей одну "#") в отдельных строках, за исключением строк разделителя. Это я могу сделать с помощью решения Telemachus.
  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; } }
}

Надеюсь, это принесет пользу и другим. Приветствия.

1 голос
/ 31 июля 2009
while (<>) {
    chomp;      # strip record separator
    if(/END/) { $f=0;}
    if (/START/) {
        s/.*START//g;
        $f=1;
    }
    print $_ ."\n" if $f;
}

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

1 голос
/ 31 июля 2009

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...