Как к 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
30
задан Kamil Kiełczewski 11 April 2019 в 19:36
поделиться

7 ответов

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

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

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

20
ответ дан 27 November 2019 в 22:58
поделиться

Это частый вопрос интервью. Вот что я написал в прошлом году, когда мне задали этот вопрос. Помните, что код, который вы получаете на 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 ( ) назад постепенно (удваивая каждый раз), пока мы не получим достаточно строк.

10
ответ дан 27 November 2019 в 22:58
поделиться

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

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

foreach ($data as $line) {
    echo $line;
}
3
ответ дан 27 November 2019 в 22:58
поделиться

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

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

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

-2
ответ дан 27 November 2019 в 22:58
поделиться

Если вы работаете в системе Linux, вы можете сделать это:

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

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

$all_lines = file('file.txt');
$last_5 = array_slice($all_lines , -5);
13
ответ дан 27 November 2019 в 22:58
поделиться

Для большого файла считывание всех строк в массив с помощью file () немного расточительно. Вот как вы можете прочитать файл и сохранить буфер из последних 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'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);
44
ответ дан 27 November 2019 в 22:58
поделиться
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);

14
ответ дан 27 November 2019 в 22:58
поделиться
Другие вопросы по тегам:

Похожие вопросы: