¿Cómo extraigo líneas entre dos delimitadores de línea en Perl?
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.
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.
- 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.
- La primera línea tiene un espacio que quiero eliminar. También lo estoy incluyendo.
- 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.
- 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.
- La primera línea tiene un espacio que quiero eliminar. También lo estoy incluyendo.
- 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
}