Book HomePHP CookbookSearch this book

18.17. Modifying a File in Place Without a Temporary File

18.17.1. Problem

You want to change a file without using a temporary file to hold the changes.

18.17.2. Solution

Read the file into memory, make the changes, and rewrite the file. Open the file with mode r+ (rb+, if necessary, on Windows) and adjust its length with ftruncate( ) after writing out changes:

// open the file for reading and writing 
$fh = fopen('pickles.txt','r+')         or die($php_errormsg);

// read the entire file into $s
$s = fread($fh,filesize('pickles.txt')) or die($php_errormsg);

// ... modify $s ...

// seek back to the beginning of the file and write the new $s
rewind($fh);
if (-1 == fwrite($fh,$s))                { die($php_errormsg); }

// adjust the file's length to just what's been written
ftruncate($fh,ftell($fh))               or die($php_errormsg);

// close the file
fclose($fh)                             or die($php_errormsg);

18.17.3. Discussion

The following code turns text emphasized with asterisks or slashes into text with HTML <b> or <i> tags:

$fh = fopen('message.txt','r+')         or die($php_errormsg);

// read the entire file into $s
$s = fread($fh,filesize('message.txt')) or die($php_errormsg);

// convert *word* to <b>word</b>
$s = preg_replace('@\*(.*?)\*@i','<b>$1</b>',$s);
// convert /word/ to <i>word</i>
$s = preg_replace('@/(.*?)/@i','<i>$1</i>',$s);

rewind($fh);
if (-1 == fwrite($fh,$s))                { die($php_errormsg); }
ftruncate($fh,ftell($fh))               or die($php_errormsg);
fclose($fh)                             or die($php_errormsg);

Because adding HTML tags makes the file grow, the entire file has to be read into memory and then processed. If the changes to a file make each line shrink (or stay the same size), the file can be processed line by line, saving memory. This example converts text marked with <b> and <i> to text marked with asterisks and slashes:

$fh = fopen('message.txt','r+')         or die($php_errormsg);

// figure out how many bytes to read
$bytes_to_read = filesize('message.txt');

// initialize variables that hold file positions
$next_read = $last_write = 0;

// keep going while there are still bytes to read
while ($next_read < $bytes_to_read) {
    
    /* move to the position of the next read, read a line, and save
     * the position of the next read */
    fseek($fh,$next_read);
    $s = fgets($fh,1048576)             or die($php_errormsg);
    $next_read = ftell($fh);

    // convert <b>word</b> to *word*
    $s = preg_replace('@<b[^>]*>(.*?)</b>@i','*$1*',$s);
    // convert <i>word</i> to /word/ 
    $s = preg_replace('@<i[^>]*>(.*?)</i>@i','/$1/',$s);

    /* move to the position where the last write ended, write the
     * converted line, and save the position for the next write */
    fseek($fh,$last_write);
    if (-1 == fwrite($fh,$s))            { die($php_errormsg); }
    $last_write = ftell($fh);
}
  
// truncate the file length to what we've already written 
ftruncate($fh,$last_write)              or die($php_errormsg);

// close the file
fclose($fh)                             or die($php_errormsg);

18.17.4. See Also

Section 11.10 and Section 11.11 for additional information on converting between ASCII and HTML; Section 18.15 discusses fseek( ) and rewind( ) in more detail; documentation on fseek( ) at http://www.php.net/fseek, rewind( ) at http://www.php.net/rewind, and ftruncate( ) at http://www.php.net/ftruncate.



Library Navigation Links

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