Book HomePHP CookbookSearch this book

18.16. Removing the Last Line of a File

18.16.1. Problem

You want to remove the last line of a file; for example, someone's added a comment to the end of your guestbook. You don't like it, so you want to get rid of it.

18.16.2. Solution

If the file is small, you can read it into an array with file( ) and then remove the last element of the array:

$lines = file('employees.txt');
array_pop($lines);
$file = join('',$lines);

18.16.3. Discussion

If the file is large, reading it into an array requires too much memory. Instead, use this code, which seeks to the end of the file and works backwards, stopping when it finds a newline:

$fh = fopen('employees.txt','r') or die("can't open: $php_errormsg");
$linebreak = $beginning_of_file = 0;

$gap = 80;
$filesize = filesize('employees.txt');
fseek($fh,0,SEEK_END);

while (! ($linebreak || $beginning_of_file)) {
    // save where we are in the file 
    $pos = ftell($fh);

    /* move back $gap chars, use rewind() to go to the beginning if
     * we're less than $gap characters into the file */ 
    if ($pos < $gap) {
        rewind($fh);
    } else {
        fseek($fh,-$gap,SEEK_CUR);
    }

    // read the $gap chars we just seeked back over 
    $s = fread($fh,$gap) or die($php_errormsg);

    /* if we read to the end of the file, remove the last character
     * since if it's a newline, we should ignore it */
    if ($pos + $gap >= $filesize) {
        $s = substr_replace($s,'',-1);
    }

    // move back to where we were before we read $gap chars into $s 
    if ($pos < $gap) {
        rewind($fh);
    } else {
        fseek($fh,-$gap,SEEK_CUR);
    }
    
    // is there a linebreak in $s ? 
    if (is_integer($lb = strrpos($s,"\n"))) {
        $linebreak = 1;
        // the last line of the file begins right after the linebreak 
        $line_end = ftell($fh) + $lb + 1;
    } 

    // break out of the loop if we're at the beginning of the file 
    if (ftell($fh) == 0) { $beginning_of_file = 1; }

}
if ($linebreak) {
    rewind($fh);
    $file_without_last_line = fread($fh,$line_end) or die($php_errormsg);
}
fclose($fh) or die("can't close: $php_errormsg");

This code starts at the end of the file and moves backwards in $gap character chunks looking for a newline. If it finds one, it knows the last line of the file starts right after that newline. This position is saved in $line_end. After the while loop, if $linebreak is set, the contents of the file from the beginning to $line_end are read into $file_without_last_line.

The last character of the file is ignored because if it's a newline, it doesn't indicate the start of the last line of the file. Consider the 10-character file whose contents are asparagus\n. It has only one line, consisting of the word asparagus and a newline character. This file without its last line is empty, which the previous code correctly produces. If it starts scanning with the last character, it sees the newline and exits its scanning loop, incorrectly printing out asparagus without the newline.

18.16.4. See Also

Section 18.15 discusses fseek( ) and rewind( ) in more detail; documentation on array_pop( ) at http://www.php.net/array-pop, fseek( ) at http://www.php.net/fseek, and rewind( ) at http://www.php.net/rewind.



Library Navigation Links

Copyright © 2003 O'Reilly & Associates. All rights reserved.