2010-04-08 3 views
7

Ho un file 200kb, quello che uso in più pagine, ma su ogni pagina ho bisogno solo di 1-2 righe di quel file, quindi come posso leggere solo queste righe di cosa ho bisogno se conosco il numero di riga?Come risparmiare memoria durante la lettura di un file in Php?

Ad esempio se ho bisogno solo della decima riga, non voglio caricare in memoria tutte le linee, solo la decima riga.

Ci scusiamo per il mio pessimo inglese!

risposta

3

A meno che non si conosca l'offset della linea, sarà necessario leggere ogni riga fino a quel punto. Puoi semplicemente buttare via le vecchie linee (che non vuoi) scorrendo il file con qualcosa come fgets(). (EDIT: Piuttosto che fgets(), vorrei suggerire @Gordon s' solution)

Forse una soluzione migliore potrebbe essere quella di utilizzare un database, come il motore di database farà il lavoro sporco di memorizzare le stringhe e consentono di (molto efficiente) ottieni una certa "linea" (non sarebbe una riga ma un record con un ID numerico, comunque equivale alla stessa cosa) senza dover leggere i record prima di esso.

+0

Si prega di commentare il downvote – Yacoby

+0

Tale database sarà più veloce è soggettivo. Se l'informazione a cui sta cercando di accedere è all'inizio del file, sarà molto più veloce. La lettura da un database sta ancora leggendo da un file. Otterrà miglioramenti dall'indice del database solo se sta cercando qualcosa di diverso dall'inizio del file. Dipende anche da ciò che sta cercando di ottenere esattamente. –

+2

Non ha mai detto che il database sarebbe più veloce. Solo che sarebbe meglio. La preoccupazione dell'OP potrebbe essere vista come un problema di memoria piuttosto che di velocità. – webbiedave

0

Passare semplicemente attraverso di essi senza memorizzare, ad es.

$i = 1; 
$file = fopen('file.txt', 'r'); 
while (!feof($file)) { 
    $line = fgets($file); // this gets whole line from the file; 
    if ($i == 10) { 
     break; // break on tenth line 
    } 
    $i ++; 
} 

L'esempio riportato sopra dovrebbe tenere memoria solo l'ultima riga ha ottenuto dal file, quindi questo è il modo più efficiente della memoria per farlo.

+1

1. dimentichi $ i ++, 2. perché non controllare se $ i == 10? – zerkms

+0

Bleh, ho sempre dimenticato di mettere gli incrementi. Per quanto riguarda il == 10 ... di nuovo, una cattiva abitudine di analizzare troppe cose in giro con ripetizioni .. veramente dispiaciuto, risolto :) – bisko

+0

stream_get_line() è più veloce di fgets() –

0

utilizzare fgets(). 10 volte :-) in questo caso non memorizzare tutte le 10 righe nella memoria

1
<?php 
    $lines = array(1, 2, 10); 

    $handle = @fopen("/tmp/inputfile.txt", "r"); 
    if ($handle) { 
     $i = 0; 
     while (!feof($handle)) { 
      $line = stream_get_line($handle, 1000000, "\n"); 

      if (in_array($i, $lines)) { 
       echo $line; 
          $line = ''; // Don't forget to clean the buffer! 
      } 

      if ($i > end($lines)) { 
       break; 
      } 

      $i++; 
     } 
     fclose($handle); 
    } 
?> 
19

Prova SplFileObject

echo memory_get_usage(), PHP_EOL;  // 333200 

$file = new SplFileObject('bible.txt'); // 996kb 
$file->seek(5000);      // jump to line 5000 (zero-based) 
echo $file->current(), PHP_EOL;   // output current line 

echo memory_get_usage(), PHP_EOL;  // 342984 vs 3319864 when using file() 

per emettere la riga corrente, è possibile utilizzare current() o semplicemente echo $file. Trovo più chiaro usare il metodo però. Puoi anche usare fgets(), ma quello otterrebbe la riga successiva.

Naturalmente, sono necessarie solo le tre linee centrali. Ho aggiunto le chiamate memory_get_usage solo per dimostrare che questo approccio non ha quasi memoria.

+0

Bello. Non ho notato che 'seek' era una linea piuttosto che basata su byte. – Yacoby

+0

+1 Preferisco questo codice perché è solo meno lavoro per il programmatore, ed è più chiaro cosa sta succedendo (cercando in determinate linee) di 'fgets'. – notJim

+0

@Yacoby c'è 'SplFileInfo :: fseek()' e 'SplFileInfo :: seek()'. Quest'ultimo è basato sulla linea, l'altro è basato su byte. 'seek()' è un metodo dall'interfaccia 'SeekableIterator'. – Gordon

0

Perché stai provando solo a caricare le prime dieci righe? Sai che caricare tutte quelle righe è di fatto un problema?

Se non hai misurato, non sai che è un problema. Non sprecare il tuo tempo per ottimizzare i non problemi. È probabile che qualsiasi modifica delle prestazioni che avrai nel non caricare l'intero file 200K sarà impercettibile, a meno che tu non sappia che il caricamento di quel file è davvero un collo di bottiglia.

2

Il contenuto del file cambia? Se è statico o relativamente statico, è possibile creare un elenco di offset in cui si desidera leggere i dati. Per esempio, se i cambiamenti dei file una volta all'anno, ma si leggono centinaia di volte al giorno, quindi è possibile pre-calcolare gli offset delle linee che si desidera e saltare direttamente in questo modo:

$offsets = array(); 
while ($line = fread($filehandle)) { .... find line 10 .... } 
$offsets[10] = ftell($filehandle); // store line 10's location 
.... find next line 
$offsets[20] = ftell($filehandle); 

e presto. Successivamente, è possibile saltare banalmente alla posizione di quella linea in questo modo:

$fh = fopen('file.txt', 'rb'); 
fseek($fh, $offsets[20]); // jump to line 20 

Ma questo potrebbe essere completamente eccessivo.Prova il benchmarking delle operazioni: confronta il tempo necessario per eseguire una "lettura di 20 righe" vecchio stile rispetto a precompute/salta.