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

Прочитать последнюю строку из файла

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

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

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

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

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

4b9b3361

Ответ 1

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

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

Ответ 2

Используйте 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--);
}

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

UPDATE: Спасибо Syntax Error за указание о пустом файле.

: - D

UPDATE2: Исправлена ​​ошибка синтаксиса, отсутствовала точка с запятой в $LastLine = ""

Ответ 3

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

Ответ 4

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

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

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

Ответ 6

этот код 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');

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

Ответ 7

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

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

$file = escapeshellarg($file); // for the security concious (should be everyone!)
$line = `tail -n 1 $file`;

Ответ 8

Можно ли оптимизировать это с другой стороны? Если это так, просто позвольте приложению журнала всегда записывать строку в файл при ее усечении (т.е. > Вместо → )

Некоторая оптимизация может быть достигнута путем "угадывания", хотя, просто откройте файл и со средней шириной строки журнала, которую вы могли бы догадаться, где будет последняя строка. Перейдите в эту позицию с помощью fseek и найдите последнюю строку.

Ответ 9

Неиспользованный код из комментариев 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; 
} 
?>

Ответ 10

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

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

Ответ 11

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

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

Ответ 12

Я бы использовал файл(), который считывает файл в массив, реверсирует массив и получает первый элемент или всплывает массив:

$last_line = array_pop (файл ($ filename));

Если вы хотите повысить производительность, попробуйте открыть файл и с помощью указателя файла перейдите в него.