Pergunta

Eu tenho um arquivo de log ASCII com algum conteúdo gostaria de extrato. Eu nunca tinha tido tempo para aprender Perl corretamente, mas eu acho que esta é uma boa ferramenta para esta tarefa.

O arquivo está estruturado da seguinte forma:

... 
... 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

Então, eu estou procurando uma maneira de extrair as linhas entre cada START e END delimitador cordas. Como posso fazer isso?

Até agora, eu só encontrei alguns exemplos sobre como imprimir uma linha com a corda START, ou outros itens de documentação que são pouco relacionadas com o que eu estou procurando.

Foi útil?

Solução

Você quer que o operador flip-flop (mais conhecido como o operador de intervalo) ..

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

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

Substituir a chamada para print com o que você realmente quer fazer (por exemplo, empurrar a linha em uma matriz, editá-lo, formatá-lo, qualquer que seja). Tenho passado as linhas que realmente têm next ou START-ing END, mas você não pode querer que o comportamento. Consulte este artigo para uma discussão sobre este operador e outros variáveis ??útil Perl especiais.

Outras dicas

A partir perlfaq6 's resposta a Como posso puxar as linhas entre dois padrões que se estão em linhas diferentes?


Você pode usar tanto exótico .. operador do Perl (documentado em perlop):

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

Se você queria linhas de texto e não, você usaria

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

Mas se você quiser ocorrências aninhados de início até END, você vai correr contra o problema descrito na pergunta nesta seção em combinar texto equilibrado.

Aqui está outro exemplo do uso de ..:

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

Como posso agarrar múltipla linhas após uma linha correspondente em Perl?

Como é que um? Naquele, a seqüência END é de R $ ^, você pode alterá-lo para a cadeia de END.

Eu também sou um novato, mas as soluções não fornecem alguns métodos ... deixe-me saber mais especificamente o que é que você quer que difere do link acima.

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

tentar escrever algum código da próxima vez

Depois de resposta de Telêmaco, as coisas começaram a derramar. Isso funciona como a solução que eu estou olhando para depois de tudo.

  1. Eu estou tentando extrair linhas delimitadas por duas cadeias (um, com uma linha que termina com "CINFILE ="; outro, com uma linha contendo um único "#") em linhas separadas, excluindo as linhas delimitadores. Isso eu posso fazer com solução de Telêmaco.
  2. A primeira linha tem um espaço Eu quero remover. Eu também estou incluindo-o.
  3. Eu também estou tentando extrair cada linha-set em arquivos separados.

Isso funciona para mim, embora o código pode ser classificado como feio; isso é porque eu sou atualmente um recém-chegado praticamente a Perl. De qualquer forma aqui vai:

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

Espero que beneficia os outros também. Felicidades.

Não muito ruim para vindo de um "newcommer virtual". Uma coisa que você poderia fazer, é colocar o "encontrado = 1 $" dentro do "if ($ encontrados == 0)" bloco de modo que você não fazer isso atribuição de cada vez entre US $ começar e US $ parada.

Outra coisa que é um pouco feio, na minha opinião, é que você abrir o mesmo FileHandler cada vez que você entrar no $ start / stop-$ bloco.

Isso mostra uma maneira de contornar isso:

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

}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top