2011-12-18 16 views
7

Esiste un modo rapido e efficiente per leggere righe specifiche di file di grandi dimensioni, senza caricarle in memoria?Lettura di una linea specifica da file di grandi dimensioni in Perl

Ho scritto uno script perl, che esegue molte forcelle e mi piacerebbe che leggessero le righe specifiche da un file.

Al momento Im utilizzando un comando esterno:

sub getFileLine { 
    my ($filePath, $lineWanted) = @_; 
    $SIG{PIPE} = '_IGNORE_'; 
    open(my $fh, '-|:utf8', "tail -q -n +$lineWanted \"$filePath\" | head -n 1"); 
    my $line = <$fh>; 
    close $fh; 
    chomp($line); 
    return $line; 
} 

sua veloce e funziona - ma forse c'è un modo più "Perl-ish", più veloce e più efficiente della memoria come questa?

Come sapete, la creazione di un processo fork in Perl duplica la memoria di processo principale, quindi se il processo principale utilizza 10 MB, la forcella utilizzerà almeno così tanto.

Il mio obiettivo è quello di mantenere il processo di fork (quindi anche il processo principale fino alla forchetta in esecuzione) l'utilizzo della memoria il più basso possibile. Ecco perché non voglio caricare l'intero file in memoria.

+2

btw, è 'IGNORE', non' _IGNORE_'. – ikegami

risposta

16

Prima di andare oltre, è importante capire come funziona fork. Quando si esegue il processo fork, il sistema operativo utilizza la semantica copy-on-write per condividere la maggior parte della memoria dei processi padre e figlio; solo la quantità di memoria che differisce tra genitore e figlio deve essere allocata separatamente.

Per leggere una sola riga di un file in Perl, ecco un modo semplice:

open my $fh, '<', $filePath or die "$filePath: $!"; 
my $line; 
while(<$fh>) { 
    if($. == $lineWanted) { 
     $line = $_; 
     last; 
    } 
} 

Questo utilizza la speciale $. variabile che contiene il numero di linea del filehandle corrente.

4

Dai un'occhiata al modulo principale Tie::File.

+0

Ho pensato che 'Tie :: File' non fosse in grado di memoria. L'OP non ha richiesto un utilizzo della memoria insufficiente? – Zaid

+0

@Zaid è in realtà abbastanza efficiente in termini di memoria; non memorizza l'intero contenuto del file in memoria, ma solo un elenco di * offset * di ogni riga. Non è gratuito (anche solo gli scalari per contenere ogni offset richiedono un po 'di spazio per riga), ma in genere è abbastanza buono da gestire i file da centinaia di megabyte con facilità. – hobbs

+0

@hobbs: sì. Da allora ho esaminato la documentazione (il commento è piuttosto vecchio ora) e rende abbastanza chiaro che non è un maiale da memoria. – Zaid

0

Non è necessario eseguire la forchetta. Come puoi immaginare, leggere una riga specifica da un file è un'operazione abbastanza comune che uno dei moduli 20k su CPAN lo fa già.

File::ReadBackwards è efficiente in termini di memoria e veloce.

Problemi correlati