Pregunta

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.

El archivo está estructurado así:

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

Entonces, estoy buscando una manera de extraer las líneas entre cada cadena de delimitador START y END . ¿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.

¿Fue útil?

Solución

Desea el operador de flip-flop (mejor conocido como el operador de rango) ..

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

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

Reemplace la llamada a print con lo que realmente quiera hacer (por ejemplo, inserte la línea en una matriz, edítela, formatee, lo que sea). Estoy next pasando las líneas que realmente tienen START o END , pero es posible que no desee ese comportamiento. Consulte este artículo para una discusión sobre este operador y otros variables especiales útiles de Perl.

Otros consejos

De respuesta de perlfaq6 a ¿Cómo puedo extraer líneas entre dos patrones que están en líneas diferentes?


Puede usar el operador algo exótico de Perl (documentado en perlop):

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

Si quisiera texto y no líneas, usaría

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

Pero si desea ocurrencias anidadas de START a END, se encontrará con el problema descrito en la pregunta de esta sección sobre texto equilibrado coincidente.

Aquí hay otro ejemplo de uso ..:

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

¿Cómo puedo tomar múltiples líneas después de una línea coincidente en Perl?

¿Cómo está ese? En ese, la cadena END es $ ^, puede cambiarla a su cadena END.

También soy un novato, pero las soluciones allí proporcionan bastantes métodos ... déjeme saber más específicamente qué es lo que desea que difiere del enlace anterior.

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

intenta escribir un código la próxima vez

."\n" if $f; }

intenta escribir un código la próxima vez

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

  1. Estoy tratando de extraer líneas delimitadas por dos cadenas (una, con una línea que termina con " CINFILE = " ;; otra, con una línea que contiene una única " # ") en líneas separadas, excluyendo el delimitador líneas. Esto lo 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 se puede clasificar como feo; Esto se debe a que actualmente soy un recién llegado a Perl. De todos modos aquí va:

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

my $start='CINFILE=

Espero que también beneficie a otros. Saludos.

; my $stop='^#

Espero que también beneficie a otros. Saludos.

; 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

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

  1. Estoy tratando de extraer líneas delimitadas por dos cadenas (una, con una línea que termina con " CINFILE = " ;; otra, con una línea que contiene una única " # ") en líneas separadas, excluyendo el delimitador líneas. Esto lo 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 se puede clasificar como feo; Esto se debe a que actualmente soy un recién llegado a Perl. De todos modos aquí va:

<*>

Espero que también beneficie a otros. Saludos.

; } $found=1; } else { if($found == 1) { close($output); $counter++; $found=0; } } }

Espero que también beneficie a otros. Saludos.

No está mal para venir de un "recién llegado virtual". Una cosa que podría hacer es poner el " $ found = 1 " dentro del " if ($ found == 0) " bloquee para que no haga esa tarea cada vez entre $ start y $ stop.

Otra cosa que es un poco fea, en mi opinión, es que abres el mismo controlador de archivos cada vez que ingresas el bloque $ start / $ stop.

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 

No está mal para venir de un "recién llegado virtual". Una cosa que podría hacer es poner el " $ found = 1 " dentro del " if ($ found == 0) " bloquee para que no haga esa tarea cada vez entre $ start y $ stop.

Otra cosa que es un poco fea, en mi opinión, es que abres el mismo controlador de archivos cada vez que ingresas el bloque $ start / $ stop.

Esto muestra una forma de evitar eso:

<*>; } } } # Find block of lines to extract }
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top