Прочитать последнюю строку из файла - PullRequest
30 голосов
/ 02 октября 2009

Я столкнулся с проблемой. У меня есть журнал в окне Linux, в котором записан вывод нескольких запущенных процессов. Этот файл иногда может быть очень большим, и мне нужно прочитать последнюю строку из этого файла.

Проблема в том, что это действие будет вызываться через AJAX-запрос довольно часто, и когда размер файла этого журнала превышает 5-6 МБ, это довольно плохо для сервера. Поэтому я думаю, что мне нужно прочитать последнюю строку, но не читать весь файл и проходить через него или загружать его в ОЗУ, потому что это просто загрузит до смерти мою коробку.

Есть ли какая-либо оптимизация для этой операции, чтобы она работала без сбоев и не вредила серверу или не убивала Apache?

Другой вариант, который у меня есть, - exec('tail -n 1 /path/to/log'), но он звучит не так хорошо.

Позднее редактирование: Я НЕ хочу помещать файл в ОЗУ, потому что он может стать огромным. fopen() не вариант.

Ответы [ 12 ]

44 голосов
/ 02 октября 2009

Это должно работать:

$line = '';

$f = fopen('data.txt', 'r');
$cursor = -1;

fseek($f, $cursor, SEEK_END);
$char = fgetc($f);

/**
 * Trim trailing newline chars of the file
 */
while ($char === "\n" || $char === "\r") {
    fseek($f, $cursor--, SEEK_END);
    $char = fgetc($f);
}

/**
 * Read until the start of file or first newline char
 */
while ($char !== false && $char !== "\n" && $char !== "\r") {
    /**
     * Prepend the new char
     */
    $line = $char . $line;
    fseek($f, $cursor--, SEEK_END);
    $char = fgetc($f);
}

echo $line;
17 голосов
/ 02 октября 2009

Используйте fseek . Вы стремитесь к последней позиции и ищите ее назад (используйте ftell , чтобы узнать текущую позицию), пока не найдете "\ n".


$fp = fopen(".....");
fseek($fp, -1, SEEK_END); 
$pos = ftell($fp);
$LastLine = "";
// Loop backword util "\n" is found.
while((($C = fgetc($fp)) != "\n") && ($pos > 0)) {
    $LastLine = $C.$LastLine;
    fseek($fp, $pos--);
}

ПРИМЕЧАНИЕ: я не проверял. Вам может потребоваться некоторая корректировка.

ОБНОВЛЕНИЕ: Спасибо Syntax Error за указание на пустой файл.

: - D

ОБНОВЛЕНИЕ2: исправлена ​​другая синтаксическая ошибка, пропущена точка с запятой в $LastLine = ""

5 голосов
/ 02 октября 2009

Вы ищете функцию fseek . Там есть рабочие примеры того, как прочитать последнюю строку файла в разделе комментариев.

3 голосов
/ 24 марта 2011
function readlastline() 
{ 
       $fp = @fopen("/dosmnt/LOGFILE.DAT", "r"); 
       $pos = -1; 
       $t = " "; 
       while ($t != "\n") { 
             fseek($fp, $pos, SEEK_END); 
             $t = fgetc($fp); 
             $pos = $pos - 1; 
       } 
       $t = fgets($fp); 
       fclose($fp); 
       return $t; 
} 

Источник: http://forums.devshed.com/php-development-5/php-quick-way-to-read-last-line-156010.html

3 голосов
/ 02 октября 2009

Если вы знаете верхнюю границу длины строки, вы можете сделать что-то вроде этого.

$maxLength = 1024;
$fp = fopen('somefile.txt', 'r');
fseek($fp, -$maxLength , SEEK_END); 
$fewLines = explode("\n", fgets($fp, $maxLength));
$lastLine = $fewLines[count($fewLines) - 1];

В ответ на редактирование : fopen просто получает дескриптор файла (т. Е. Убедитесь, что он существует, у процесса есть разрешение, позволяет узнать, что процесс использует файл и т. Д.). В этом примере только 1024 символа из файла будут считаны в память.

2 голосов
/ 30 июня 2014

это код Ionuț G. Stan

Я немного изменил ваш код и сделал его функцией для повторного использования

function read_last_line ($file_path){



$line = '';

$f = fopen($file_path, 'r');
$cursor = -1;

fseek($f, $cursor, SEEK_END);
$char = fgetc($f);

/**
* Trim trailing newline chars of the file
*/
while ($char === "\n" || $char === "\r") {
    fseek($f, $cursor--, SEEK_END);
    $char = fgetc($f);
}

/**
* Read until the start of file or first newline char
*/
while ($char !== false && $char !== "\n" && $char !== "\r") {
    /**
     * Prepend the new char
     */
    $line = $char . $line;
    fseek($f, $cursor--, SEEK_END);
    $char = fgetc($f);
}

return $line;
}

echo read_last_line ('log.txt');

вы получите эту последнюю строку

1 голос
/ 02 октября 2009

Ваша проблема похожа на эту

Лучший способ избежать загрузки всего файла в память:

$file = escapeshellarg($file); // for the security concious (should be everyone!)
$line = `tail -n 1 $file`;
0 голосов
/ 03 октября 2016

Здесь приведена компиляция ответов, заключенных в функцию, которая может указать, сколько строк должно быть возвращено.

function getLastLines($path, $totalLines) {
  $lines = array();

  $fp = fopen($path, 'r');
  fseek($fp, -1, SEEK_END);
  $pos = ftell($fp);
  $lastLine = "";

  // Loop backword until we have our lines or we reach the start
  while($pos > 0 && count($lines) < $totalLines) {

    $C = fgetc($fp);
    if($C == "\n") {
      // skip empty lines
      if(trim($lastLine) != "") {
        $lines[] = $lastLine;
      }
      $lastLine = '';
    } else {
      $lastLine = $C.$lastLine;
    }
    fseek($fp, $pos--);
  }

  $lines = array_reverse($lines);

  return $lines;
}
0 голосов
/ 10 июня 2016

Это мое решение только с одной петлей

        $line = '';
        $f = fopen($file_path, 'r');
        $cursor = 0 ;
        do  {
            fseek($f, $cursor--, SEEK_END);
            $char = fgetc($f);
            $line = $char.$line;
        } while (
                $cursor > -1 || (
                 ord($char) !== 10 &&
                 ord($char) !== 13
                )
        );
0 голосов
/ 24 марта 2011

непроверенный код из комментариев http://php.net/manual/en/function.fseek.php

jim at lfchosting dot com 05-Nov-2003 02:03
Here is a function that returns the last line of a file.  This should be quicker than reading the whole file till you get to the last line.  If you want to speed it up a bit, you can set the $pos = some number that is just greater than the line length.  The files I was dealing with were various lengths, so this worked for me. 

<?php 
function readlastline($file) 
{ 
        $fp = @fopen($file, "r"); 
        $pos = -1; 
        $t = " "; 
        while ($t != "\n") { 
              fseek($fp, $pos, SEEK_END); 
              $t = fgetc($fp); 
              $pos = $pos - 1; 
        } 
        $t = fgets($fp); 
        fclose($fp); 
        return $t; 
} 
?>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...