2013-06-02 9 views
5

Ho un file che sto leggendo con PHP. Voglio cercare alcune righe che iniziano con uno spazio bianco e poi alcune parole chiave che sto cercando (ad esempio, "nome_progetto:") e quindi modificare altre parti di tale linea.Qual è il modo migliore (più efficiente) per cercare il contenuto in un file e modificarlo con PHP?

Attualmente, il mio modo di gestire questa situazione è quello di leggere l'intero file in una variabile stringa, manipolare quella stringa e poi scrivere il tutto indietro al file, completamente sostituendo l'intero file (via fopen(filepath, "wb") e fwrite()), ma questo si sente inefficiente. C'è un modo migliore?

+1

"Il migliore" è soggettivo. Dalle ragioni vicine "_Spettiamo che le risposte siano supportate da fatti, riferimenti o competenze specifiche, ma questa domanda probabilmente solleciterà dibattiti, discussioni, sondaggi o discussioni estese." "Considereresti di migliorare la tua domanda selezionando un metodo particolare e spiegando come non soddisfa le tue esigenze?Ciò consentirà di fornire risposte specifiche, piuttosto che soggettive. –

+4

@GeorgeCummins Il tuo commento non si applica qui. Questa è una tipica domanda di programmazione – hek2mgl

+1

@Baba Sei sicuro che i tuoi tentativi siano più veloci di quello [I've supposto] (http://stackoverflow.com/a/16887051/171318)? Nota che un semplice 'rename()' è molto veloce. Preparerà alcuni benchmark :) Si noti inoltre che la ricerca in cui la stringa deve essere sostituita non è nota nella maggior parte degli scenari applicativi – hek2mgl

risposta

3

Aggiornamento: Dopo aver terminato la mia funzione, ho avuto il tempo di confrontarlo. Ho usato un file di grandi dimensioni 1GB per il test ma i risultati sono insoddisfacenti: |

Sì, l'allocazione memoria di picco è significativamente minore:

  • soluzione standard: 1,86 GB
  • personalizzato soluzione: 653 KB (4096 byte buffersize)

Ma rispetto al seguente soluzione c'è solo un leggero aumento delle prestazioni:

ini_set('memory_limit', -1); 

file_put_contents(
    'test.txt', 
    str_replace('the', 'teh', file_get_contents('test.txt')) 
); 

la scrittura t sopra ha richiesto ~ 16 secondi, la soluzione personalizzata ha richiesto ~ 13 secondi.

Riprendi: la soluzione di custodia è leggermente più veloce su file di grandi dimensioni e consuma molta meno memoria (!!!).

Inoltre, se si desidera eseguire questo in un ambiente di server Web, la soluzione personalizzata è migliore in quanto molti script concorrenti potrebbero consumare l'intera memoria disponibile del sistema.


risposta originale:

L'unica cosa che mi viene in mente, è quello di leggere il file in blocchi che si inseriscono i file system Dimensione blocco e scrivere il contenuto o modificati i contenuti di nuovo ad un file temporaneo . Dopo aver terminato l'elaborazione, utilizzare rename() per sovrascrivere il file originale.

Ciò ridurrebbe il picco di memoria e dovrebbe essere significativamente più veloce se il file è molto grande.

Nota: su un sistema Linux è possibile ottenere la dimensione del blocco del file system utilizzando:

sudo dumpe2fs /dev/yourdev | grep 'Block size' 

ho ottenuto 4096

Ecco che arriva la funzione:

function freplace($search, $replace, $filename, $buffersize = 4096) { 

    $fd1 = fopen($filename, 'r'); 
    if(!is_resource($fd1)) { 
     die('error opening file'); 
    } 

    // the tempfile can be anywhere but on the same partition as the original 
    $tmpfile = tempnam('.', uniqid()); 
    $fd2 = fopen($tmpfile, 'w+'); 

    // we store len(search) -1 chars from the end of the buffer on each loop 
    // this is the maximum chars of the search string that can be on the 
    // border between two buffers 
    $tmp = ''; 
    while(!feof($fd1)) { 
     $buffer = fread($fd1, $buffersize); 
     // prepend the rest from last one 
     $buffer = $tmp . $buffer; 
     // replace 
     $buffer = str_replace($search, $replace, $buffer); 
     // store len(search) - 1 chars from the end of the buffer 
     $tmp = substr($buffer, -1 * (strlen($search)) + 1); 
     // write processed buffer (minus rest) 
     fwrite($fd2, $buffer, strlen($buffer) - strlen($tmp)); 
    }; 

    if(!empty($tmp)) { 
     fwrite($fd2, $tmp); 
    } 

    fclose($fd1); 
    fclose($fd2); 
    rename($tmpfile, $filename); 
} 

chiamare in questo modo :

freplace('foo', 'bar', 'test.txt'); 
+2

Cosa succede se la stringa di ricerca si trova a cavallo dei buffer di lettura? – Jon

+0

Grazie per il commento. Ho aggiornato il post. Sì, per avere questa prova di proiettile ha bisogno di più attenzione. – hek2mgl

+0

@ hek2mgl c'è un vantaggio nel farlo in questo modo invece di aprire con "x +" e quindi combinare i passaggi? –

Problemi correlati