Ryan
Ryan

Reputation: 15270

How can I most efficiently read the first n lines of a large text file in php?

So far I'm able to return the entire file, or an empty string. But not the first n lines of a file as expected.

echo head('/path/to/my_file.txt',100); // returns '', or in other versions `1\n1\n...`

function head($filepath, $lines = 1, $adaptive = true) {

    // Open file
    $f = @fopen($filepath, "rb");
    if ($f === false) return false;

    // Sets buffer size
    if (!$adaptive) $buffer = 4096;
    else $buffer = ($lines < 2 ? 64 : ($lines < 10 ? 512 : 4096));

    // Start reading
    $output = '';
    while($line = fgets($f,$buffer) !== false) {
        if (feof($f)) break;
        $output .= $line . "\n";
    }

    // Close file and return
    fclose($f);
    return trim($output);

}

In contrast, the following tail function (source) works perfectly:

function tail($filepath, $lines = 1, $adaptive = true) {

    // Open file
    $f = @fopen($filepath, "rb");
    if ($f === false) return false;

    // Sets buffer size
    if (!$adaptive) $buffer = 4096;
    else $buffer = ($lines < 2 ? 64 : ($lines < 10 ? 512 : 4096));

    // Jump to last character
    fseek($f, -1, SEEK_END);

    // Read it and adjust line number if necessary
    // (Otherwise the result would be wrong if file doesn't end with a blank line)
    if (fread($f, 1) != "\n") $lines -= 1;

    // Start reading
    $output = '';
    $chunk = '';

    // While we would like more
    while (ftell($f) > 0 && $lines >= 0) {

        // Figure out how far back we should jump
        $seek = min(ftell($f), $buffer);

        // Do the jump (backwards, relative to where we are)
        fseek($f, -$seek, SEEK_CUR);

        // Read a chunk and prepend it to our output
        $output = ($chunk = fread($f, $seek)) . $output;

        // Jump back to where we started reading
        fseek($f, -mb_strlen($chunk, '8bit'), SEEK_CUR);

        // Decrease our line counter
        $lines -= substr_count($chunk, "\n");

    }

    // While we have too many lines
    // (Because of buffer size we might have read too many)
    while ($lines++ < 0) {

        // Find first newline and remove all text before that
        $output = substr($output, strpos($output, "\n") + 1);

    }

    // Close file and return
    fclose($f);
    return trim($output);

}

Upvotes: 0

Views: 1433

Answers (2)

Halayem Anis
Halayem Anis

Reputation: 7795

// here the correction => apply parenthesis before ...
while(($line = fgets($f)) !== false) {
    if (feof($f)) break;
    if ($lines-- == 0) break;
    $output .= $line . "\n";
}

Upvotes: 2

fbas
fbas

Reputation: 1674

You just need to keep a count of the number of lines read, and abort after $lines is reached.

 $i = 0;
 while($line = fgets($file) !== false) {
        if (feof($file)) break;
        $output .= $line . "\n";
        if ($i++ >= $lines) {
          break;
        }
    }

Upvotes: 1

Related Questions