Read a file backwards line by line using fseek

Viewed 31110

How do I read a file backwards line by line using fseek?

code can be helpful. must be cross platform and pure php.

many thanks in advance

regards

Jera

10 Answers

I know this has been answered already but I found another, maybe faster, way.

// Read last 5000 chars of 'foo.log' 

if(file_exists('foo.log') && $file = fopen('foo.log', 'r')) {
    fseek($file, -5000, SEEK_END);

    $text = stream_get_line($file, 5000); 

    var_dump($text);

    fclose($file);
}

Here's a drop in replacement(ish) for fgets($fp) called fgetsr() that reads lines from a file in reverse order.

This code is verbatim so you should (famous last words) be able to copy it into a file on your server and run it. Though you may well need to change the filename in the fopn() call.

<?php
    header('Content-Type: text/plain');
    $fp = fopen('post.html', 'r');
    
    while($line = fgetsr($fp)) {
        echo $line;
    }







    // Read a line from the file but starting from the end
    //
    // @param $fp integer The file pointer
    //
    function fgetsr($fp)
    {
        // Make this variable persistent inside this function
        static $seeked;
        
        // The line buffer that will eventually be returned
        $line = '';

        // Initially seek to the end of the file
        if (!$seeked) {
            fseek($fp, -1, SEEK_END);
            $seeked = true;
        }
        
        // Loop through all of the characters in the file
        while(strlen($char = fgetc($fp)) {

            // fgetc() advances that pointer so go back TWO places
            // instead of one
            fseek($fp, -2, SEEK_CUR);

            //
            // Check for a newline (LF). If a newline is found
            // then break out of the function and return the
            // line that's stored in the buffer.
            //
            // NB The first line in the file (ie the last to
            //    be read)has a special case
            //
            if (ftell($fp) <= 0) {
                fseek($fp, 0, SEEK_SET);
                $line = fgets($fp);
                fseek($fp, 0, SEEK_SET);
                return $line;
            } else if ($char === "\n") {
                $line = strrev($line);
                return $line . "\n";
            } else {
                $line .= $char;
            }
        }
    }
?>

Functions to read a file line-by-line in reverse:

function revfopen($filepath, $mode)
{
    $fp = fopen($filepath, $mode);
    fseek($fp, -1, SEEK_END);
    if (fgetc($fp) !== PHP_EOL) {
        fseek($fp, 1, SEEK_END);
    }

    return $fp;
}

function revfgets($fp)
{
    $s = '';
    while (true) {
        if (fseek($fp, -2, SEEK_CUR) === -1) {
            return false;
        }
        if (($c = fgetc($fp)) === PHP_EOL) {
            break;
        }
        $s = $c . $s;
    }

    return $s;
}

Example use case: parse a long file until some date:

$fp = revfopen('/path/to/file', 'r');

$buffer = '';
while (($line = revfgets($fp)) !== false) {
    if (strpos($line, '05-10-2021') === 0) {
        break;
    }

    array_unshift($buffer, $line);
}

echo implode("\n", $buffer);
Related