¿Cómo leer solo 5 última línea del archivo de texto en PHP?

Tengo un archivo llamado “archivo.txt” que se actualiza al agregarle líneas.

Lo estoy leyendo con este código:

$fp = fopen("file.txt", "r"); $data = ""; while(!feof($fp)) { $data .= fgets($fp, 4096); } echo $data; 

y aparece una gran cantidad de líneas Solo quiero repetir las últimas 5 líneas del archivo

Cómo puedo hacer eso ?


El file.txt es así:

 11111111111111 22222222222 33333333333333 44444444444 55555555555555 66666666666 

Código no probado, pero debería funcionar:

 $file = file("filename.txt"); for ($i = max(0, count($file)-6); $i < count($file); $i++) { echo $file[$i] . "\n"; } 

Calling max manejará que el archivo tenga menos de 6 líneas.

Para un archivo grande, leer todas las líneas en una matriz con el archivo () es un poco derrochador. Así es como puedes leer el archivo y mantener un buffer de las últimas 5 líneas:

 $lines=array(); $fp = fopen("file.txt", "r"); while(!feof($fp)) { $line = fgets($fp, 4096); array_push($lines, $line); if (count($lines)>5) array_shift($lines); } fclose($fp); 

Puede optimizar esto un poco más con algunas heurísticas sobre la longitud de línea probable buscando una posición, por ejemplo, aproximadamente 10 líneas desde el final, y yendo más atrás si eso no produce 5 líneas. Aquí hay una implementación simple que demuestra que:

 //how many lines? $linecount=5; //what's a typical line length? $length=40; //which file? $file="test.txt"; //we double the offset factor on each iteration //if our first guess at the file offset doesn't //yield $linecount lines $offset_factor=1; $bytes=filesize($file); $fp = fopen($file, "r") or die("Can't open $file"); $complete=false; while (!$complete) { //seek to a position close to end of file $offset = $linecount * $length * $offset_factor; fseek($fp, -$offset, SEEK_END); //we might seek mid-line, so read partial line //if our offset means we're reading the whole file, //we don't skip... if ($offset<$bytes) fgets($fp); //read all following lines, store last x $lines=array(); while(!feof($fp)) { $line = fgets($fp); array_push($lines, $line); if (count($lines)>$linecount) { array_shift($lines); $complete=true; } } //if we read the whole file, we're done, even if we //don't have enough lines if ($offset>=$bytes) $complete=true; else $offset_factor*=2; //otherwise let's seek even further back } fclose($fp); var_dump($lines); 
 function ReadFromEndByLine($filename,$lines) { /* freely customisable number of lines read per time*/ $bufferlength = 5000; $handle = @fopen($filename, "r"); if (!$handle) { echo "Error: can't find or open $filename
\n"; return -1; } /*get the file size with a trick*/ fseek($handle, 0, SEEK_END); $filesize = ftell($handle); /*don't want to get past the start-of-file*/ $position= - min($bufferlength,$filesize); while ($lines > 0) { if ($err=fseek($handle,$position,SEEK_END)) { /* should not happen but it's better if we check it*/ echo "Error $err: something went wrong
\n"; fclose($handle); return $lines; } /* big read*/ $buffer = fread($handle,$bufferlength); /* small split*/ $tmp = explode("\n",$buffer); /*previous read could have stored a partial line in $aliq*/ if ($aliq != "") { /*concatenate current last line with the piece left from the previous read*/ $tmp[count($tmp)-1].=$aliq; } /*drop first line because it may not be complete*/ $aliq = array_shift($tmp); $read = count($tmp); if ( $read >= $lines ) { /*have read too much!*/ $tmp2 = array_slice($tmp,$read-$n); /* merge it with the array which will be returned by the function*/ $lines = array_merge($tmp2,$lines); /* break the cycle*/ $lines = 0; } elseif (-$position >= $filesize) { /* haven't read enough but arrived at the start of file*/ //get back $aliq which contains the very first line of the file $lines = array_merge($aliq,$tmp,$lines); //force it to stop reading $lines = 0; } else { /*continue reading...*/ //add the freshly grabbed lines on top of the others $lines = array_merge($tmp,$lines); $lines -= $read; //next time we want to read another block $position -= $bufferlength; //don't want to get past the start of file $position = max($position, -$filesize); } } fclose($handle); return $lines; }

Esto será rápido para archivos más grandes pero mucho código para una tarea simple, si hay ARCHIVOS GRANDES, usa esto

ReadFromEndByLine (‘myFile.txt’, 6);

Si estás en un sistema Linux, puedes hacer esto:

 $lines = `tail -5 /path/to/file.txt`; 

De lo contrario, tendrá que contar las líneas y tomar los últimos 5, algo así como:

 $all_lines = file('file.txt'); $last_5 = array_slice($all_lines , -5); 

Esta es una pregunta de entrevista común. Esto es lo que escribí el año pasado cuando me hicieron esta pregunta. Recuerde que el código que obtiene en Stack Overflow tiene licencia con Creative Commons Share-Alike con atribución requerida .

 = $numLines? $c-$numLines: 0; for (; $i<$c; ++$i) { if ($pos = strpos($lines[$i], $searchString)) { echo $lines[$i]; } } 

Esta solución hace una suposición sobre la longitud de línea máxima. El entrevistador me preguntó cómo iba a resolver el problema si no podía hacer esa suposición, y tuve que adaptar las líneas que eran potencialmente más largas que cualquier longitud máxima que elegí.

Le dije que cualquier proyecto de software tiene que hacer ciertas suposiciones, pero podría probar si $c fuera menor que el número deseado de líneas, y si no lo es, fseek() retrocede aún más gradualmente (doblando cada vez) hasta que lo hagamos obtener suficientes líneas

Esto no usa el file() por lo que será más eficiente para archivos grandes;

 = 0 ) { $c = fgetc($fp); if($c == "\n" || $c == "\r"){ $lines--; if( $revers ){ $read[$i] = strrev($read[$i]); $i++; } } if( $revers ) $read[$i] .= $c; else $read .= $c; $offset--; } fclose ($fp); if( $revers ){ if($read[$i] == "\n" || $read[$i] == "\r") array_pop($read); else $read[$i] = strrev($read[$i]); return implode('',$read); } return strrev(rtrim($read,"\n\r")); } //if $revers=false function return-> //line 1000: i am line of 1000 //line 1001: and i am line of 1001 //line 1002: and i am last line //but if $revers=true function return-> //line 1002: and i am last line //line 1001: and i am line of 1001 //line 1000: i am line of 1000 ?> 

La mayoría de las opciones aquí suponen leer el archivo en la memoria y luego trabajar con filas. Esta no sería una buena idea, si el archivo es demasiado grande

Creo que la mejor manera es usar alguna utilidad de sistema operativo, como ‘cola’ en Unix.

 exec('tail -3 /logs/reports/2017/02-15/173606-arachni-2415.log', $output); echo $output; // 2017-02-15 18:03:25 [*] Path Traversal: Analyzing response ... // 2017-02-15 18:03:27 [*] Path Traversal: Analyzing response ... // 2017-02-15 18:03:27 [*] Path Traversal: Analyzing response ... 

La función de archivo de PHP () lee todo el archivo en una matriz. Esta solución requiere la menor cantidad de tipeo:

 $data = array_slice(file('file.txt'), -5); foreach ($data as $line) { echo $line; } 

Abrir archivos grandes con file() puede generar una gran matriz, reservando una gran cantidad de memoria.

Puede reducir el costo de memoria con SplFileObject ya que itera a través de cada línea.

Use el método de seek (de seekableiterator ) para buscar la última línea. Debería restar el valor de la clave actual por 5.

Para obtener la última línea, use PHP_INT_MAX . (Sí, esto es una solución).

 $file = new SplFileObject('large_file.txt', 'r'); $file->seek(PHP_INT_MAX); $last_line = $file->key(); $lines = new LimitIterator($file, $last_line - 5, $last_line); print_r(iterator_to_array($lines)); 

Esta función funcionará para archivos REALMENTE grandes en 4 GB. La velocidad proviene de leer una gran cantidad de datos en lugar de 1 byte a la vez y contar líneas.

 // Will seek backwards $n lines from the current position function seekLineBackFast($fh, $n = 1){ $pos = ftell($fh); if ($pos == 0) return false; $posAtStart = $pos; $readSize = 2048*2; $pos = ftell($fh); if(!$pos){ fseek($fh, 0, SEEK_SET); return false; } // we want to seek 1 line before the line we want. // so that we can start at the very beginning of the line while ($n >= 0) { if($pos == 0) break; $pos -= $readSize; if($pos <= 0){ $pos = 0; } // fseek returns 0 on success and -1 on error if(fseek($fh, $pos, SEEK_SET)==-1){ fseek($fh, 0, SEEK_SET); break; } $data = fread($fh, $readSize); $count = substr_count($data, "\n"); $n -= $count; if($n < 0) break; } fseek($fh, $pos, SEEK_SET); // we may have seeked too far back // so we read one line at a time forward while($n < 0){ fgets($fh); $n++; } // just in case? $pos = ftell($fh); if(!$pos) fseek($fh, 0, SEEK_SET); // check that we have indeed gone back if ($pos >= $posAtStart) return false; return $pos; } 

Después de ejecutar la función anterior, puede hacer fgets () en un bucle para leer cada línea a la vez desde $ fh.

Puedes usar mi pequeña biblioteca de ayuda (2 funciones)

https://github.com/jasir/file-helpers

Entonces solo usa:

 //read last 5 lines $lines = \jasir\FileHelpers\FileHelpers::readLastLines($pathToFile, 5); 

He probado este. Esto funciona para mi.

 function getlast($filename,$linenum_to_read,$linelength){ // this function takes 3 arguments; if (!$linelength){ $linelength = 600;} $f = fopen($filename, 'r'); $linenum = filesize($filename)/$linelength; for ($i=1; $i<=($linenum-$linenum_to_read);$i++) { $data = fread($f,$linelength); } echo "
"; for ($j=1; $j<=$linenum_to_read+1;$j++) { echo fread($f,$linelength); } echo "

The filesize is:".filesize("$filename"); } getlast("file.txt",6,230); ?>

Menos cantidad de ram, y salidas bien. Estoy de acuerdo con Paul Dixon …

 $lines=array(); $fp = fopen("userlog.txt", "r"); while(!feof($fp)) { $line = fgets($fp, 4096); array_push($lines, $line); if (count($lines)>25) array_shift($lines); } fclose($fp); while ($a <= 10) { $a++; echo "
".$lines[$a]; }
 $dosya = "../dosya.txt"; $array = explode("\n", file_get_contents($dosya)); $reversed = array_reverse($array); for($x = 0; $x < 6; $x++) { echo $reversed[$x]; } 

Si sus líneas están separadas por un CR o LF, debería intentar explotar su variable de $ data:

 $lines = explode("\n", $data); 

$ lines debe terminar siendo una matriz y puede calcular el número de registros utilizando sizeof () y solo obtener los últimos 5.

esto es leer las últimas 10 líneas del archivo de texto

 $data = array_slice(file('logs.txt'),10); foreach ($data as $line) { echo $line."
"; }