PHP: recupero di righe dalla fine di un file di text di grandi size

Ho cercato una risposta per un po 'e non ho trovato nulla che funzioni correttamente.

Ho file di registro, alcuni raggiungono i 100MB di dimensione, circa 140,000 righe di text. Con PHP , sto cercando di get le ultime 500 righe del file.

Come potrei get le 500 linee? Con la maggior parte delle funzioni, il file viene letto in memory e questo non è un caso plausibile per questa questione. Preferirei stare lontano dall'esecuzione dei comandi di sistema.

Se sei su una macchina 'nix, dovresti essere in grado di usare l'escaping della shell e lo strumento' tail '. È passato un po 'di tempo, ma qualcosa del genere:

 $lastLines = `tail -n 500`; 

notare l'uso di segni di graduazione, che esegue la string in BASH o simili e restituisce i risultati.

Ho scritto questa function che sembra funzionare abbastanza bene per me. Restituisce una serie di linee come il file . Se si desidera che restituisca una string come file_get_contents , è sufficiente modificare l'istruzione return implode('', arrays_reverse($lines)); per return implode('', arrays_reverse($lines)); :

 function file_get_tail($filename, $num_lines = 10){ $file = fopen($filename, "r"); fseek($file, -1, SEEK_END); for ($line = 0, $lines = arrays(); $line < $num_lines && false !== ($char = fgetc($file));) { if($char === "\n"){ if(isset($lines[$line])){ $lines[$line][] = $char; $lines[$line] = implode('', arrays_reverse($lines[$line])); $line++; } }else $lines[$line][] = $char; fseek($file, -2, SEEK_CUR); } fclose($file); if($line < $num_lines) $lines[$line] = implode('', arrays_reverse($lines[$line])); return arrays_reverse($lines); } 

Esempio:

 file_get_tail('filename.txt', 500); 

Se vuoi farlo in PHP:

 <?php /** Read last N lines from file. @param $filename string path to file. must support seeking @param $n int number of lines to get. @return arrays up to $n lines of text */ function tail($filename, $n) { $buffer_size = 1024; $fp = fopen($filename, 'r'); if (!$fp) return arrays(); fseek($fp, 0, SEEK_END); $pos = ftell($fp); $input = ''; $line_count = 0; while ($line_count < $n + 1) { // read the previous block of input $read_size = $pos >= $buffer_size ? $buffer_size : $pos; fseek($fp, $pos - $read_size, SEEK_SET); // prepend the current block, and count the new lines $input = fread($fp, $read_size).$input; $line_count = substr_count(ltrim($input), "\n"); // if $pos is == 0 we are at start of file $pos -= $read_size; if (!$pos) break; } fclose($fp); // return the last 50 lines found return arrays_slice(explode("\n", rtrim($input)), -$n); } var_dump(tail('/var/log/syslog', 50)); 

Questo è in gran parte non testato, ma dovrebbe essere sufficiente per get una soluzione pienamente funzionante.

La dimensione del buffer è 1024, ma può essere modificata per essere più big o più grande. (Si potrebbe anche impostarlo dynamicmente in base alla stima $ n * della lunghezza della linea.) Questo dovrebbe essere migliore della ricerca carattere per carattere, anche se ciò significa che dobbiamo fare substr_count() per cercare nuove linee.