2009-07-31 9 views
11

Tengo un archivo de registro ASCII con algún contenido que me gustaría extraer. Nunca me he tomado el tiempo para aprender Perl correctamente, pero creo que esta es una buena herramienta para esta tarea.¿Cómo se extraen las líneas entre dos delimitadores de línea en Perl?

El archivo está estructurado de la siguiente manera:

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

lo tanto, estoy buscando una manera de extraer las líneas entre cada START y END cadenas delimitador. ¿Cómo puedo hacer esto?

Hasta ahora, solo he encontrado algunos ejemplos sobre cómo imprimir una línea con la cadena START u otros elementos de documentación que están algo relacionados con lo que estoy buscando.

+0

Utilice el partido/g global en lugar de dejar que se detiene en la terminación de línea. – Lazarus

+0

que significó/s? AFAIK/g es ** coincidencia ** múltiple. –

+1

Esta es una pregunta duplicada. Ver .... http://stackoverflow.com/questions/296366/how-can-i-extract-lines-of-text-from-a-file/296672#296672 – draegtun

Respuesta

22

desea que el operador del flip-flop (más conocido como el operador de rango) ..

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

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

reemplaza la llamada a print con lo que realmente quiere hacer (por ejemplo, empujar la línea en una matriz, editar it, format it, whatever) Estoy next -ing más allá de las líneas que en realidad tienen START o END, pero puede que no desee que el comportamiento. Vea this article para una discusión sobre este operador y otras variables especiales útiles de Perl.

+0

¡¡¡Funciona para mí !! Como quiero excluir las líneas con los delimitadores, puedo canalizar la salida a través de grep -v, por ejemplo. BTW, en la primera línea después de START, ¿cómo podría eliminar el primer carácter en una línea? – jbatista

+3

+1 para flip-flop –

+1

La versión de una sola línea: perl -ne 'print if /START/../END/' –

1

How can I grab multiple lines after a matching line in Perl?

¿Cómo es que uno? En esa, la cadena END es $ ^, puede cambiarla a su cadena END.

también soy un novato, pero las soluciones no proporcionan unos métodos bastante ... que me haga saber más específicamente qué es lo que quiere que se diferencia desde el enlace anterior.

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

tratar de escribir algo de código próxima vez ronda

+0

Entiendo, y hubiera escrito algún código si ya hubiera comenzado a aprender Perl. He logrado pasar con awk y sed hasta ahora. Pero de todos modos, gracias por tu consejo. – jbatista

1

Después respuesta de Telémaco, las cosas comenzaron a llegar a cabo. Esto funciona como la solución que estoy buscando después de todo.

  1. Estoy intentando extraer líneas delimitadas por dos cadenas (uno, con una línea que termina con "CINFILE ="; otro, con una línea que contiene un único "#") en líneas separadas, excluyendo las líneas delimitadores . Esto puedo hacer con la solución de Telémaco.
  2. La primera línea tiene un espacio que quiero eliminar. También lo estoy incluyendo.
  3. También estoy tratando de extraer cada conjunto de líneas en archivos separados.

Esto funciona para mí, aunque el código puede clasificarse como feo; esto se debe a que actualmente soy prácticamente un recién llegado a Perl. De todos modos aquí va:

#!/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 beneficie a otros también. Saludos.

5

De respuesta perlfaq6 's de How can I pull out lines between two patterns that are themselves on different lines?


Se puede utilizar de Perl tanto exótica ..operador (documentado en perlop):

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

Si quería texto y no las líneas, se usaría

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

Pero si quieres ocurrencias anidados de START través de FIN, podrás correr contra el problema descrito en la pregunta en esta sección sobre la coincidencia de texto equilibrado.

Aquí es otro ejemplo del uso ..:

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

No está mal para proveniente de una "newcommer virtual". Una cosa que podría hacer es colocar el "$ found = 1" dentro del bloque "if ($ found == 0)" para que no haga esa asignación cada vez entre $ start y $ stop.

Otra cosa que es un poco fea, en mi opinión, es que abre el mismo manejador de archivos cada vez que ingresa $ start/$ stop-block.

Esto muestra una forma de evitar eso:

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

} 
+0

Gracias. Ahora siento que debería desperdiciar^H^H^H^H^Hase algo de tiempo para aprender Perl correctamente. Mi historial está en C, algo de C++ y algo de Fortran, por lo que me parece familiar. – jbatista

+0

Por cierto, admito que fui laxo en la apertura de muchos archivos, mi principal preocupación en ese momento era conseguir algo que funcionara, aunque no demasiado bien. – jbatista

Cuestiones relacionadas