Прочитать последнюю строку из файла
Я столкнулся с проблемой. У меня есть журнал в ящике Linux, в котором записывается вывод нескольких запущенных процессов. Иногда этот файл может стать действительно большим, и мне нужно прочитать последнюю строку из этого файла.
Проблема заключается в том, что это действие будет вызвано через запрос AJAX довольно часто, и когда размер файла этого журнала превышает 5-6 МБ, он скорее не подходит для сервера. Поэтому я думаю, что мне нужно прочитать последнюю строку, но не читать весь файл и не передавать его или загружать в ОЗУ, потому что это будет просто загружать в мою коробку.
Есть ли какая-либо оптимизация для этой операции, чтобы она работала бесперебойно, а не повредила сервер или убила Apache?
Другим вариантом, который у меня есть, является exec('tail -n 1 /path/to/log')
, но это звучит не так хорошо.
Далее отредактируйте: Я НЕ хочу помещать файл в ОЗУ, потому что он может стать огромным. fopen()
не является вариантом.
Ответы
Ответ 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 символа из файла.
Ответ 5
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
Ответ 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));
Если вы хотите повысить производительность, попробуйте открыть файл и с помощью указателя файла перейдите в него.