从文件中读取最后一行

时间:2009-10-02 15:09:55

标签: php file file-io

我遇到了一个问题。我在Linux机器上登录,其中写了几个正在运行的进程的输出。这个文件有时会非常大,我需要读取该文件的最后一行。

问题是这个动作会经常通过AJAX请求调用,当该日志的文件大小超过5-6MB时,它对服务器来说并不好。所以我想我必须阅读最后一行,但不要读取整个文件并通过它或将其加载到RAM中,因为这只会加载到我的盒子中。

此操作是否有任何优化,以便它运行顺畅,不会损害服务器或杀死Apache?

我拥有的其他选项是exec('tail -n 1 /path/to/log'),但听起来不太好。

稍后编辑:我不想将文件放在RAM中,因为它可能会变得很大。 fopen()不是一种选择。

14 个答案:

答案 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));

如果你想要性能,请尝试打开文件并使用文件指针导航到它。