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.