2009-06-24 7 views
11

Estoy analizando un archivo grande en Perl línea por línea (terminado por \ n), pero cuando llego a cierta palabra clave, diga "OBJETIVO", necesito tomar todas las líneas entre OBJETIVO y la siguiente completamente vacía línea.¿Cómo puedo tomar varias líneas después de una línea coincidente en Perl?

Por lo tanto, dado un segmento de un archivo:

Line 1
Line 2
Línea 3
Línea 4 Target
Line 5 Grab esta línea
Line 6 Grab esta línea
\ n

Debería ser:
Línea 4 Objetivo
L ine 5 Tome esta línea
Línea 6 Tome esta línea

La razón por la que estoy teniendo problemas es que ya estoy revisando el archivo línea por línea; ¿Cómo cambio lo que delimito a mitad del proceso de análisis?

Respuesta

23

¿Quieres algo como esto:

my @grabbed; 
while (<FILE>) { 
    if (/TARGET/) { 
     push @grabbed, $_; 
     while (<FILE>) { 
      last if /^$/; 
      push @grabbed, $_; 
     } 
    } 
} 
+0

Ah, gracias, yo no estaba seguro de si mientras dentro de otro, mientras que estaba bien en Perl :) – Dirk

+0

@ Michael es simplemente otro readline call, entonces, sí, está bien. perldoc -f readline –

+2

Si el identificador no apunta a un archivo real, sino más bien algo así como STDIN, puede hacer que el interno obtenga un eof y termine y luego el exterior mientras continúa leyendo hasta que * it * obtenga un eof. Pruébalo con: perl -wle'print "read a"; while (<>) {print "read b"; while (<>) {print "read b"} print "read a"} ' – ysth

2
while(<FILE>) 
{ 
    if (/target/i) 
    { 
     $buffer .= $_; 
     while(<FILE>) 
     { 
      $buffer .= $_; 
      last if /^\n$/; 
     } 
    } 
} 
0

Si no le importa código generado automáticamente feo, y suponiendo que lo que desea líneas entre TARGET y la siguiente línea vacía y quieres todo el otras líneas que se cayó, se puede utilizar la salida de este comando:

s2p -ne '/TARGET/,/^$/p' 

(Sí, esto es un indicio de que este problema se suele resolver con mayor facilidad en sed:. - P)

+2

Vea la respuesta de gbacon. Esto podría escribirse como "perl -ne 'print if/TARGET/../^ $ /'" que es más o menos exactamente lo que tiene. – user55400

+0

¡Gracias por el aviso! Pocas veces vuelvo para verificar las respuestas de otras personas, por lo que es bueno que haya una respuesta claramente superior. –

10

La respuesta corta: delimitador de línea en Perl es $/, por lo que al alcanzar la meta, se puede establecer $/ a "\n\n", lea la "línea" siguiente, a continuación, establezca de nuevo a "\ n" ... et voilà!

Ahora, para el más largo:. Si se utiliza el módulo de English (que da nombres sensibles a todas las variables magia de Perl, a continuación, $/ se llama $RS o $INPUT_RECORD_SEPARATOR Si utiliza IO::Handle, entonces IO::Handle->input_record_separator("\n\n") funcionará

. y si está haciendo esto como parte de un pedazo más grande de código, no se olvide de localizar ya sea (usando local $/; en el ámbito apropiado) o para retroceder $/ a su valor original de "\n".

+0

Me gusta cómo explicaste la forma de hacerlo sin dar código. Es un poco más largo, pero al final es mejor para el lector hacer algo similar en el futuro. –

+1

Si 'usas inglés;' (lo cual no hago, pero lo que sea que flote tu bote) asegúrate de 'usar inglés '-no_match_vars';', de lo contrario obtendrás un golpe de rendimiento con expresiones regulares. –

+1

@Chris Lutz tiene razón, solo asumí que si usaba inglés, leería los documentos. – mirod

0

Si sólo quiero un bucle (modificando Dave Código de Hinton):

my @grabbed; 
my $grabbing = 0; 
while (<FILE>) { 
    if (/TARGET/) { 
     $grabbing = 1; 
    } elsif(/^$/) { 
     $grabbing = 0; 
    } 
    if ($grabbing) { 
     push @grabbed, @_; 
    } 
} 
+0

eche un vistazo a algunos de los otros ejemplos aquí ... $ flags deben evitarse ya que este es el código 'perl', y como tal, debería estar usando perl-isms. –

+0

@ Ape-inago ¿Puedes explicarlo? (Me acabo de dar cuenta de que uso 'banderas' en el código en cualquier otro lugar) – Dirk

+8

Use banderas si eso es lo que tiene sentido para usted. 'Cualquier nivel de dominio del idioma es aceptable en la cultura Perl. No enviaremos a la policía de idiomas después de ti. Un script de Perl es "correcto" si hace el trabajo antes de que tu jefe te despida. - Larry Wall – ysth

1
use strict; 
use warnings; 

my $inside = 0; 
my $data = ''; 
while (<DATA>) { 
    $inside = 1 if /Target/; 
    last if /^$/ and $inside; 
    $data .= $_ if $inside; 
} 

print '[' . $data . ']'; 

__DATA__ 
Line 1 
Line 2 
Line 3 
Line 4 Target 
Line 5 Grab this line 
Line 6 Grab this line 

Next Line 

Editar para fijar la condición de salida de acuerdo con la siguiente nota.

+0

Estaría en contra de las banderas, ¡pero este es uno de los más claros que he visto hasta ahora! –

+0

d0h! Debería cambiar eso por "last if/^ $/and $ inside;" para manejar el caso donde hay una línea en blanco antes del objetivo. – telesphore4

14

El range operator es ideal para este tipo de tarea:

$ cat try 
#! /usr/bin/perl 

while (<DATA>) { 
    print if /\btarget\b/i .. /^\s*$/ 
} 

__DATA__ 
Line 1 
Line 2 
Line 3 
Line 4 Target 
Line 5 Grab this line 
Line 6 Grab this line 

Nope 
Line 7 Target 
Linu 8 Yep 

Nope again 

$ ./try 
Line 4 Target 
Line 5 Grab this line 
Line 6 Grab this line 

Line 7 Target 
Linu 8 Yep 
+1

compare esto con la primera solución – user105033

4

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


Se puede utilizar tanto exótica .. operador de Perl (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, que se encontrará con el problema descrito en la pregunta en esta sección en 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 $. 
} 
0
while (<IN>) { 
print OUT if (/Target/../^$/) ; 
} 
Cuestiones relacionadas