我遇到了一个问题。我在Linux机器上登录,其中写了几个正在运行的进程的输出。这个文件有时会非常大,我需要读取该文件的最后一行。
问题是这个动作会经常通过AJAX请求调用,当该日志的文件大小超过5-6MB时,它对服务器来说并不好。所以我想我必须阅读最后一行,但不要读取整个文件并通过它或将其加载到RAM中,因为这只会加载到我的盒子中。
此操作是否有任何优化,以便它运行顺畅,不会损害服务器或杀死Apache?
我拥有的其他选项是exec('tail -n 1 /path/to/log')
,但听起来不太好。
稍后编辑:我不想将文件放在RAM中,因为它可能会变得很大。 fopen()
不是一种选择。
答案 0 :(得分:43)
这应该有效:
$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;
答案 1 :(得分:17)
使用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
UPDATE2:Fixxed另一个语法错误,在$LastLine = ""
答案 2 :(得分:5)
您正在寻找fseek功能。有一些工作示例说明如何在那里的注释部分读取文件的最后一行。
答案 3 :(得分:3)
如果您知道行长的上限,则可以执行此类操作。
$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个字符将被读入内存。
答案 4 :(得分:3)
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
答案 5 :(得分:2)
这是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');
你会得到最后一行
答案 6 :(得分:1)
您的问题与this one
类似避免将整个文件加载到内存中的最佳方法似乎是:
$file = escapeshellarg($file); // for the security concious (should be everyone!)
$line = `tail -n 1 $file`;
答案 7 :(得分:0)
是否有可能从另一方面优化这一点? 如果是这样,只需让日志记录应用程序始终将该行记录到文件中,同时截断它(即>而不是>>)
虽然可以通过“猜测”来实现某些优化,只需打开文件并使用平均日志行宽度就可以猜出最后一行的位置。用fseek跳到那个位置,找到最后一行。
答案 8 :(得分:0)
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;
}
?>
答案 9 :(得分:0)
这是我的解决方案只有一个循环
$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
)
);
答案 10 :(得分:0)
以下是一个包含在函数中的答案汇编,该函数可以指定应该返回多少行。
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;
}
答案 11 :(得分:0)
通过传递$initial_pos
,该函数将允许您从末尾逐行读取最后一行或(可选)整个文件,这将告诉您从何处开始从末尾读取文件(负整数)。 / p>
function file_read_last_line($file, $initial_pos = -1) {
$fp = is_string($file) ? fopen($file, 'r') : $file;
$pos = $initial_pos;
$line = '';
do {
fseek($fp, $pos, SEEK_END);
$char = fgetc($fp);
if ($char === false) {
if ($pos === $initial_pos) return false;
break;
}
$pos = $pos - 1;
if ($char === "\r" || $char === "\n") continue;
$line = $char . $line;
} while ($char !== "\n");
if (is_string($file)) fclose($file);
return $line;
}
然后,阅读最后一行:
$last_line = file_read_last_line('/path/to/file');
要从末尾逐行读取整个文件:
$fp = fopen('/path/to/file', 'r');
$pos = -1;
while (($line = file_read_last_line($fp, $pos)) !== false) {
$pos += -(strlen($line) + 1);
echo 'LINE: ' . $line . "\n";
}
fclose($fp);
答案 12 :(得分:0)
一个文件块一个块地向后遍历,找到新行后停止,返回最后一个“\n”之后的所有内容。
function get_last_line($file) {
if (!is_file($file)) return false;
$fileSize = filesize($file);
$bufferSize = 1024; // <------------------ Change buffer size here.
$bufferSize = ($fileSize > $bufferSize) ? $bufferSize : $fileSize;
$fp = fopen($file, 'r');
$position = $fileSize - $bufferSize;
$data = "";
while (true) {
fseek($fp, $position);
$chunk = fread($fp, $bufferSize);
$data = $chunk.$data;
$pos = strrchr($data, "\n");
if ($pos !== false) return substr($pos, 1);
if ($position <= 0) break;
$position -= $bufferSize;
if ($position <=0) $position = 0;
}
// whole file is 'the last line' :)
return $data;
}
您可以自己定义块的长度。
更小的块 = 更少的内存使用,更多的迭代。
更大的块 = 更大的内存使用量,更少的迭代。
答案 13 :(得分:-3)
我会使用file()将文件读入数组,反转数组并获取第一个元素或弹出数组:
$ last_line = array_pop(file($ filename));
如果你想要性能,请尝试打开文件并使用文件指针导航到它。