Как читать только 5 последних строк текстового файла в PHP?

У меня есть файл с именем «file.txt», который обновляется, добавляя к нему строки.

Я читаю его по этому коду:

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

и появляется огромное количество строк. Я просто хочу повторить последние 5 строк файла

Как я могу это сделать ?


file.txt выглядит так:

 11111111111111 22222222222 33333333333333 44444444444 55555555555555 66666666666 

Solutions Collecting From Web of "Как читать только 5 последних строк текстового файла в PHP?"

Непроверенный код, но должен работать:

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

Вызов max будет обрабатывать файл менее 6 строк.

Для большого файла чтение всех строк в массив с файлом () немного расточительно. Вот как вы можете прочитать файл и сохранить буфер из последних 5 строк:

 $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); в $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); 

Вы могли бы оптимизировать это немного больше с некоторой эвристикой относительно вероятной длины линии, стремясь к позиции, скажем, приблизительно 10 строк от конца, и идите назад, если это не дает 5 строк. Вот простая реализация, которая демонстрирует, что:

 //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); в //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<br/>\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<br/>\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; } 

Это будет быстро для больших файлов, но для этого достаточно простого кода, если есть БОЛЬШИЕ ФАЙЛЫ, используйте это

ReadFromEndByLine ( 'myfile.txt', 6);

Это общий вопрос для интервью. Вот что я написал в прошлом году, когда меня задали этот вопрос. Помните, что код, который вы получаете на Stack Overflow, имеет лицензию на использование Creative Commons Share-Alike с необходимой атрибуцией .

 <?php /** * Demonstrate an efficient way to search the last 100 lines of a file * containing roughly ten million lines for a sample string. This should * function without having to process each line of the file (and without making * use of the “tail” command or any external system commands). */ $filename = '/opt/local/apache2/logs/karwin-access_log'; $searchString = 'index.php'; $numLines = 100; $maxLineLength = 200; $fp = fopen($filename, 'r'); $data = fseek($fp, -($numLines * $maxLineLength), SEEK_END); $lines = array(); while (!feof($fp)) { $lines[] = fgets($fp); } $c = count($lines); $i = $c >= $numLines? $c-$numLines: 0; for (; $i<$c; ++$i) { if ($pos = strpos($lines[$i], $searchString)) { echo $lines[$i]; } } 

Это решение делает предположение о максимальной длине линии. Интервьюер спросил меня, как я разрешу проблему, если я не смогу сделать это предположение, и должен был разместить линии, которые были потенциально длиннее любой максимальной длины, которую я выбрал.

Я сказал ему, что любой проект программного обеспечения должен делать определенные предположения, но я мог бы проверить, было ли $c меньше желаемого количества строк, а если нет, fseek() назад дополнительно инкрементно (удваивая каждый раз), пока мы не сделаем получить достаточно строк.

Если вы используете систему Linux, вы можете сделать это:

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

В противном случае вам придется подсчитывать строки и принимать последние 5, что-то вроде:

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

Это не использует file() поэтому он будет более эффективным для огромных файлов;

 <?php function read_backward_line($filename, $lines, $revers = false) { $offset = -1; $c = ''; $read = ''; $i = 0; $fp = @fopen($filename, "r"); while( $lines && fseek($fp, $offset, SEEK_END) >= 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 ?> 

Большинство опций здесь предполагают читать файл в памяти, а затем работать со строками. Это не будет хорошей идеей, если файл слишком большой

Я думаю, что лучший способ – использовать некоторую ОС-утилиту, такую ​​как «tail» в 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 ... 

Функция file () PHP считывает весь файл в массив. Это решение требует наименьшего количества ввода:

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

Открытие больших файлов с помощью file() может генерировать большой массив, сохраняя значительный объем памяти.

Вы можете уменьшить стоимость памяти с помощью SplFileObject поскольку она выполняет SplFileObject через каждую строку.

Используйте метод seek ( seekableiterator ) для получения последней строки. Затем вы должны вычесть текущее значение ключа на 5.

Чтобы получить последнюю строку, используйте PHP_INT_MAX . (Да, это обходной путь.)

 $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)); 

Эта функция будет работать для ДЕЙСТВИТЕЛЬНО больших файлов под 4 ГБ. Скорость исходит от чтения большого фрагмента данных вместо 1 байта за раз и подсчета строк.

 // 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; } 

После выполнения функции вы можете просто сделать fgets () в цикле, чтобы читать каждую строку за раз от $ fh.

Вы можете использовать мою небольшую вспомогательную библиотеку (2 функции)

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

Затем просто используйте:

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

Я проверил это. Меня устраивает.

 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 "<pre>"; for ($j=1; $j<=$linenum_to_read+1;$j++) { echo fread($f,$linelength); } echo "</pre><hr />The filesize is:".filesize("$filename"); } getlast("file.txt",6,230); ?> 

Наименьшее количество бара и выходы хорошо. Я согласен с Полом Диксоном …

 $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 "<br>".$lines[$a]; } в $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 "<br>".$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]; } 

Если ваши линии разделены CR или LF, вы попытаетесь взломать переменную $ data:

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

$ lines должно быть массивом, и вы можете определить количество записей с помощью sizeof () и просто получить последние 5.

это прочитано последние 10 строк из текстового файла

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