У меня есть файл с именем «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
Непроверенный код, но должен работать:
$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/>"; }