Read last line from file

2020-01-28 05:13发布

I've been bumping into a problem. I have a log on a Linux box in which is written the output from several running processes. This file can get really big sometimes and I need to read the last line from that file.

The problem is this action will be called via an AJAX request pretty often and when the file size of that log gets over 5-6MB it's rather not good for the server. So I'm thinking I have to read the last line but not to read the whole file and pass through it or load it in RAM because that would just load to death my box.

Is there any optimization for this operation so that it run smooth and not harm the server or kill Apache?

Other option that I have is to exec('tail -n 1 /path/to/log') but it doesn't sound so good.

Later edit: I DO NOT want to put the file in RAM because it might get huge. fopen() is not an option.

标签: php file file-io
12条回答
beautiful°
2楼-- · 2020-01-28 05:42

This should work:

$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;
查看更多
我只想做你的唯一
3楼-- · 2020-01-28 05:42

this the code of Ionuț G. Stan

i modified your code a little and made it a function for reuseability

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

you will get that last line

查看更多
霸刀☆藐视天下
4楼-- · 2020-01-28 05:42

Here is a compilation of the answers here wrapped into a function which can specify how many lines should be returned.

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;
}
查看更多
We Are One
5楼-- · 2020-01-28 05:46

Untested code from the comments of 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; 
} 
?>
查看更多
该账号已被封号
6楼-- · 2020-01-28 05:47

If you know the upper bound of line length you could do something like this.

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

In response to the edit: fopen just acquires a handle to the file (i.e. make sure it exists, process has permission, lets os know a process is using the file, etc...). In this example only 1024 characters from the file will be read into memory.

查看更多
成全新的幸福
7楼-- · 2020-01-28 05:53

Use fseek. You seek to the last position and seek it backward (use ftell to tell the current position) until you find a "\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--);
}

NOTE: I've not tested. You may need some adjustment.

UPDATE: Thanks Syntax Error for pointing out about empty file.

:-D

UPDATE2: Fixxed another Syntax Error, missing semicolon at $LastLine = ""

查看更多
登录 后发表回答