while array perl foreach

while - perl foreach array



Extraiga y filtre un rango de lĂ­neas de la entrada usando Perl (6)

Soy bastante nuevo para Perl y tengo algunos problemas al omitir líneas usando un ciclo foreach . Quiero copiar algunas líneas de un archivo de texto a uno nuevo.

Cuando las primeras palabras de una línea son FIRST ITERATION , omita dos líneas más e imprima todo lo que sigue hasta que se encuentre el final del archivo o una línea vacía.

Intenté buscar una publicación similar, pero nadie habla de trabajar con archivos de texto.

Esta es la forma en que pensé

use 5.010; use strict; use warnings; open( INPUT, "xxx.txt" ) or die("Could not open log file."); open( OUT, ">>yyy.txt" ); foreach my $line (<INPUT>) { if ( $line =~ m/^FIRST ITERATION/ ) { # print OUT } } close(OUT); close(INFO);

Intenté usar next y $line++ pero mi programa imprime solo la línea que comienza con FIRST ITERATION .

Puedo intentar usar un bucle for pero no sé cuántas líneas puede tener mi archivo, ni sé cuántas líneas hay entre "First Iteration" y la siguiente línea vacía.


Consejo: Use STDIN y STDOUT en lugar de archivos, esto le permitirá cambiarlos sin modificar el guión

Código:

#!/usr/bin/perl use 5.010; use strict; use warnings; open(INPUT, "xxx.txt" ) or die "Could not open log file: $!."; open(OUT, ">yyy.txt") or die "Could not open output file: $!"; while( my $line = <INPUT> ) { if ( $line =~ m/^FIRST ITERATION/) { <INPUT>; # skip line <INPUT>; # skip line while( $line = <INPUT>) # print till empty line { last if $line eq "/n"; print OUT $line; } }; }; close (OUT); close (INPUT);


Estás en el camino correcto. Lo que necesita usar es el operador de flip-flop (que es básicamente el operador de rango) .. Te alternará entre dos partidos, por lo que obtienes todo lo que está en medio. Después de eso, se trata de hacer un seguimiento de las líneas que desea omitir.

Así que, básicamente, estamos buscando FIRST ITERATION y una línea vacía, y tomamos todo lo que hay entre ellos. $skip se usa para recordar cuántas líneas se saltaron. Comienza en 0 y se incrementa para las primeras dos líneas después de que comenzamos a estar en el flip-flop if bloque. En el caso else , donde estamos después del flip-flop, se restablece a 0 para que podamos comenzar de nuevo con el siguiente bloque.

Como sabes cómo abrir y escribir archivos, me saltearé eso.

use strict; use warnings; my $skip = 0; while (<DATA>) { if (/^FIRST ITERATION$/ .. /^$/) { next if $skip++ <= 2; print $_; } else { $skip = 0; } } __DATA__ FIRST ITERATION skip1 skip2 foo bar baz don''t print this

El resultado de esto es:

foo bar baz

Para seguir con su propio código, aquí hay una solución muy detallada que usa foreach y no flip-flop. Hace lo mismo, solo con muchas más palabras.

my $skip = 0; # skip lines my $match = 0; # keep track of if we''re in between the borders foreach my $line (<DATA>) { if ( $line =~ m/^FIRST ITERATION/ ) { $match = 1; # we are inside the match next; } if ($line =~ m/^$/) { $match = 0; # we are done matching next; } if ($match) { $skip++; # count skip-lines if ($skip <= 2) { next; # ... and skip the first two } print $line; # this is the content we want } }


La forma más simple es procesar el archivo una línea a la vez y mantener un indicador de estado que se establece en 1 si la línea actual comienza con FIRST ITERATION y 0 si está en blanco; de lo contrario, se incrementa si ya es positiva, de modo que proporciona un recuento del número de línea dentro del bloque actual

Esta solución espera la ruta al archivo de entrada como un parámetro en la línea de comando e imprime su salida en STDOUT, por lo que tendrá que redirigir la salida al archivo en la línea de comando según sea necesario.

Tenga en cuenta que el patrón de expresiones regulares //S/ comprueba si hay un carácter no en blanco en cualquier lugar de la línea actual, por not //S/ es verdadero si la línea está vacía o todos los caracteres en blanco

use strict; use warnings; my $lines = 0; while ( <> ) { if ( /^FIRST ITERATION/ ) { $lines = 1; } elsif ( not //S/ ) { $lines = 0; } elsif ( $lines > 0 ) { ++$lines; } print if $lines > 3; }

Esto se puede simplificar sustancialmente utilizando el operador de rango integrado de Perl, que mantiene su propio estado interno y devuelve el número de veces que se ha evaluado. Entonces lo anterior puede ser escrito

use strict; use warnings; while ( <> ) { my $s = /^FIRST ITERATION/ ... not //S/; print if $s and $s > 3; }

Y el último puede ser reescrito como un programa de línea de comando de una línea como este

$ perl -ne ''$s = /^FIRST ITERATION/ ... not //S/; print if $s and $s > 3'' myfile.txt


Usando el modo de párrafo (que devuelve bloques separados por líneas en blanco en lugar de líneas):

local $/ = ""; # Paragraph mode. while (<>) { s//n/K/n+//; # Get rid of trailing empty lines. print /^FIRST ITERATION/n[^/n]*/n[^/n]*/n(.*)/ms; }


Usando el operador flip-flop:

while (<>) { if (my $line_num = /^FIRST ITERATION$/ .. /^$/) { print if $line_num > 3 && $line_num !~ /E0/; } }

$line_num !~ /E0/ es verdadero cuando el flip-flop está FIRST ITERATION (es decir, para la primera línea vacía después de FIRST ITERATION ). Esto se verifica para evitar imprimir la línea en blanco.


Use un contador adicional, que dirá en qué condición imprime la línea. Algo como esto:

$skipCounter = 3;

Y en foreach:

if ($skipCounter == 2) { // print OUT } if ( $line =~ m/^FIRST ITERATION/) { $skipCounter = 0; } $skipCounter++;