Como posso extrair linhas entre dois delimitadores de linha em Perl?
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.
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.
- 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.
- A primeira linha tem um espaço Eu quero remover. Eu também estou incluindo-o.
- 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
}