2009-06-24 6 views
11

Sto analizzando un file di grandi dimensioni in Perl line-by-line (terminato da \ n), ma quando raggiungo una determinata parola chiave, ad esempio "TARGET", devo prendere tutte le linee tra TARGET e il prossimo completamente vuoto linea.Come posso prendere più linee dopo una linea corrispondente in Perl?

Quindi, dato un segmento di un file:

Linea 1
Linea 2
Linea 3
Linea 4 Obiettivo
Linea 5 Grab questa linea
Linea 6 Grab questa linea
\ n

Deve diventare:
Riga 4 Destinazione
L ine 5 Prendi questa riga
Linea 6 Prendi questa riga

Il motivo per cui ho problemi è che sto già esaminando il file riga per riga; come posso modificare ciò che delimino a metà del processo di analisi?

risposta

23

Volete qualcosa di simile:

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

Ah, grazie, io non ero sicuro se mentre all'interno di un altro mentre era ok in perl :) – Dirk

+0

@ Michael è solo un altro chiamata readline, quindi, sì, è OK. perldoc -f readline –

+2

Se l'handle non punta a un file reale, ma piuttosto a qualcosa come STDIN, puoi avere l'interno mentre ottieni un eof e termina e poi l'esterno mentre continui a leggere fino a * it * ottiene un eof. Provalo con: perl -wle'print "leggi un"; while (<>) {stampa "leggi b"; while (<>) {print "read b"} stampa "read a"} ' – ysth

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

Se non ti dispiace codice generato automaticamente brutto, e supponendo che si desidera solo linee tra TARGET e la successiva riga vuota, e desidera che tutto il altre linee per essere eliminato, è possibile utilizzare l'output di questo comando:

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

(Sì, questo è un suggerimento che questo problema è di solito molto più facilmente risolto in sed:. - P)

+2

Vedere la risposta di gbacon. Questo potrebbe essere scritto come "perl -ne 'print se/TARGET/../^ $ /'" che è più o meno esattamente quello che hai. – user55400

+0

Grazie per l'heads-up! Raramente vado di nuovo a cercare le risposte di altre persone, quindi è bene che ci sia una risposta chiaramente più elevata data. –

10

La risposta breve: delimitatore di riga in Perl è $/, in modo che quando si colpisce di destinazione, è possibile impostare $/-"\n\n", leggere la "linea" successiva, quindi impostare di nuovo a "\ n" ... et Ecco!

Ora per il più lungo:. Se si utilizza il modulo English (che dà nomi sensibili a tutte le variabili magia di Perl, quindi $/ è chiamato $RS o $INPUT_RECORD_SEPARATOR Se si utilizza IO::Handle, quindi IO::Handle->input_record_separator("\n\n") funzionerà

. e se si sta facendo questo come parte di un pezzo più grande di codice, non dimenticate di entrambi localizzare (utilizzando local $/; in ambito appropriato) o per impostare di nuovo $/ al suo valore originale di "\n".

+0

Mi piace come hai spiegato il modo di farlo senza dare il codice. È un po 'più lungo, ma alla fine il lettore è meglio fare qualcosa di simile in futuro. –

+1

Se si usa l'inglese; '(cosa che non so, ma qualunque cosa faccia galleggiare la vostra barca) assicuratevi di' usare l'inglese '-no_match_vars' ;, altrimenti otterrete un successo nelle prestazioni con le espressioni regolari. –

+1

@Chris Lutz hai ragione, ho solo pensato che se usi l'inglese, allora leggevi i documenti. – mirod

0

Se solo desidera un ciclo (modificando Dave il codice di Hinton):

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

dai un'occhiata ad alcuni degli altri esempi qui ... $ flags dovrebbero essere evitati poiché questo è il codice 'perl', e come tale dovresti usare perl-isms. –

+0

@ Ape-inago Puoi spiegare? (Ho appena notato che uso 'bandiere' nel codice altrove) – Dirk

+8

Usa le bandiere se è quello che ha senso per te. "Qualsiasi livello di competenza linguistica è accettabile nella cultura Perl. Non manderemo la polizia linguistica dopo di te. Uno script Perl è "corretto" se riesce a completare il lavoro prima che il tuo capo ti licenzi. " - 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 

Modifica per risolvere la condizione di uscita secondo la nota che segue.

+0

Sarei contro le bandiere, ma questo è uno dei più chiari che ho visto finora! –

+0

d0h! Dovrei cambiarlo in "last if/^ $/and $ inside;" per gestire il caso in cui vi è una riga vuota prima del bersaglio. – telesphore4

14

Il range operator è l'ideale per questo tipo di operazione:

$ 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

confronta questo con la prima soluzione – user105033

4

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


È possibile utilizzare .. operatore di un po' esotica del Perl (documentato in perlop):

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

Se si desidera il testo e non le linee, si può usare

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

Ma se volete occorrenze nidificati di AVVIO attraverso fine, si incorrerà in su contro il problema descritto nella domanda in questa sezione sulla corrispondenza testo equilibrato.

Ecco un altro esempio di utilizzo ..:

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