Подтвердить что ты не робот

Как читать только 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
4b9b3361

Ответ 1

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

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

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

Ответ 2

Для большого файла чтение всех строк в массив с файлом() немного расточительно. Здесь вы можете прочитать файл и сохранить буфер из последних 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);

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

//how many lines?
$linecount=5;

//what 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 seek even further back

}
fclose($fp);

var_dump($lines);

Ответ 3

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

Ответ 4

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

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

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

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

Ответ 5

Это распространенный вопрос интервью. Вот что я написал в прошлом году, когда мне задавали этот вопрос. Помните, что код, который вы получаете в 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). 
 * Attribution: https://stackoverflow.com/a/2961731/3389585
 */

$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() возвращался назад постепенно (удваивая каждый раз), пока мы не сделаем получить достаточно строк.

Ответ 6

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

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

Ответ 7

Это не использует 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
?>

Ответ 8

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

Вы можете уменьшить стоимость памяти с помощью 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));

Ответ 9

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

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

foreach ($data as $line) {
    echo $line;
}

Ответ 10

Эта функция будет работать для ДЕЙСТВИТЕЛЬНО больших файлов под 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.

Ответ 11

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

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

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

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

Ответ 12

Вот БЫСТРЫЙ метод для БОЛЬШИХ файлов - я разрабатываю ответ Уоллеса Макстерса (если хотите повысить голосование - делайте это на его ответе), оборачивая его код в удобную функцию и добавляя обратную функцию

function readLastLines($filename, $num, $reverse = false)
{
    $file = new \SplFileObject($filename, 'r');
    $file->seek(PHP_INT_MAX);
    $last_line = $file->key();
    $lines = new \LimitIterator($file, $last_line - $num, $last_line);
    $arr = iterator_to_array($lines);
    if($reverse) $arr = array_reverse($arr);
    return implode('',$arr);
}

// use it by
$lines = readLastLines("file.txt", 5) // return string with 5 last lines

Ответ 13

Я тестировал этот. Это работает для меня.

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


?>

Ответ 14

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

$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];
}

Ответ 15

$dosya = "../dosya.txt";
$array = explode("\n", file_get_contents($dosya));
$reversed = array_reverse($array);
for($x = 0; $x < 6; $x++) 
{
    echo $reversed[$x];
}

Ответ 16

Вот мое решение:

/**
 *
 * Reads N lines from a file
 *
 * @param type $file       path
 * @param type $maxLines   Count of lines to read
 * @param type $reverse    set to true if result should be reversed.
 * @return string
 */
public function readLinesFromFile($file, $maxLines, $reverse=false)
{
    $lines = file($file);

    if ($reverse) {
        $lines = array_reverse($lines);
    }

    $tmpArr = array();

    if ($maxLines > count($lines))
        exit("\$maxLines ist größer als die Anzahl der Zeilen in der Datei.");

    for ($i=0; $i < $maxLines; $i++) {
        array_push($tmpArr, $lines[$i]);
    }

    if ($reverse) {
        $tmpArr = array_reverse($tmpArr);
    }

    $out = "";
    for ($i=0; $i < $maxLines; $i++) {
        $out .= $tmpArr[$i] . "</br>";
    }

    return $out;
}

Ответ 17

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

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

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

Ответ 18

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

$data = array_slice(file('logs.txt'),10);

    foreach ($data as $line) 

    {

        echo $line."<br/>";
    }