PHP How to Write Data to Middle of File Without Rewriting File

Using php, how to insert text without overwriting to the beginning of a text file

I'm not entirely sure of your question - do you want to write data and not have it over-write the beginning of an existing file, or write new data to the start of an existing file, keeping the existing content after it?

To insert text without over-writing the beginning of the file, you'll have to open it for appending (a+ rather than r+)

$file=fopen(date("Y-m-d").".txt","a+") or exit("Unable to open file!");

if ($_POST["lastname"] <> "")
{
fwrite($file,$_POST["lastname"]."\n");
}

fclose($file);

If you're trying to write to the start of the file, you'll have to read in the file contents (see file_get_contents) first, then write your new string followed by file contents to the output file.

$old_content = file_get_contents($file);
fwrite($file, $new_content."\n".$old_content);

The above approach will work with small files, but you may run into memory limits trying to read a large file in using file_get_conents. In this case, consider using rewind($file), which sets the file position indicator for handle to the beginning of the file stream.
Note when using rewind(), not to open the file with the a (or a+) options, as:

If you have opened the file in append ("a" or "a+") mode, any data you write to the file will always be appended, regardless of the file position.

Replacing a line in a file without rewriting the entire file (in PHP)

If the replacement is EXACTLY the same size as the original line, then you can simply fwrite() at that location in the file and all's well. But if it's a different length (shorter OR longer), you will have to rewrite the portion of the file that comes AFTER the replacement.

There is no way around this. Shorter 'new' lines will leave a gap in the file, and longer 'new' lines would overwrite the first part of the next line.

Basically, you're asking if it's possible to insert a piece of wood in the middle of another board without having to move the original board around.

Inserting a string into the middle of a TXT file with PHP

Explode the file by new line.

That will give you a nice array with each line.

Loop through and find the point of insertion and add the text you want to add.

Then implode it back and write it to the file back.

C write in the middle of a binary file without overwriting any existing content

Here's a function extend_file_and_insert() that does the job, more or less.

#include <sys/stat.h>
#include <unistd.h>

enum { BUFFERSIZE = 64 * 1024 };

#define MIN(x, y) (((x) < (y)) ? (x) : (y))

/*
off_t is signed
ssize_t is signed
size_t is unsigned

off_t for lseek() offset and return
size_t for read()/write() length
ssize_t for read()/write() return
off_t for st_size
*/

static int extend_file_and_insert(int fd, off_t offset, char const *insert, size_t inslen)
{
char buffer[BUFFERSIZE];
struct stat sb;
int rc = -1;

if (fstat(fd, &sb) == 0)
{
if (sb.st_size > offset)
{
/* Move data after offset up by inslen bytes */
size_t bytes_to_move = sb.st_size - offset;
off_t read_end_offset = sb.st_size;
while (bytes_to_move != 0)
{
ssize_t bytes_this_time = MIN(BUFFERSIZE, bytes_to_move);
ssize_t rd_off = read_end_offset - bytes_this_time;
ssize_t wr_off = rd_off + inslen;
lseek(fd, rd_off, SEEK_SET);
if (read(fd, buffer, bytes_this_time) != bytes_this_time)
return -1;
lseek(fd, wr_off, SEEK_SET);
if (write(fd, buffer, bytes_this_time) != bytes_this_time)
return -1;
bytes_to_move -= bytes_this_time;
read_end_offset -= bytes_this_time; /* Added 2013-07-19 */
}
}
lseek(fd, offset, SEEK_SET);
write(fd, insert, inslen);
rc = 0;
}
return rc;
}

(Note the additional line added 2013-07-19; it was a bug that only shows when the buffer size is smaller than the amount of data to be copied up the file. Thanks to malat for pointing out the error. Code now tested with BUFFERSIZE = 4.)

This is some small-scale test code:

#include <fcntl.h>
#include <string.h>

static const char base_data[] = "12345";
typedef struct Data
{
off_t posn;
const char *data;
} Data;
static const Data insert[] =
{
{ 2, "456" },
{ 4, "XxxxxxX" },
{ 12, "ZzzzzzzzzzzzzzzzzzzzzzzzX" },
{ 22, "YyyyyyyyyyyyyyyY" },
};
enum { NUM_INSERT = sizeof(insert) / sizeof(insert[0]) };

int main(void)
{
int fd = open("test.dat", O_RDWR | O_TRUNC | O_CREAT, 0644);
if (fd > 0)
{
ssize_t base_len = sizeof(base_data) - 1;
if (write(fd, base_data, base_len) == base_len)
{
for (int i = 0; i < NUM_INSERT; i++)
{
off_t length = strlen(insert[i].data);
if (extend_file_and_insert(fd, insert[i].posn, insert[i].data, length) != 0)
break;
lseek(fd, 0, SEEK_SET);
char buffer[BUFFERSIZE];
ssize_t nbytes;
while ((nbytes = read(fd, buffer, sizeof(buffer))) > 0)
write(1, buffer, nbytes);
write(1, "\n", 1);
}
}
close(fd);
}
return(0);
}

It produces the output:

12456345
1245XxxxxxX6345
1245XxxxxxX6ZzzzzzzzzzzzzzzzzzzzzzzzZ345
1245XxxxxxX6ZzzzzzzzzzYyyyyyyyyyyyyyyYzzzzzzzzzzzzzzZ345

It should be tested on some larger files (ones bigger than BUFFERSIZE, but it would be sensible to test with a BUFFERSIZE a lot smaller than 64 KiB; I used 32 bytes and it seemed to be OK). I've only eyeballed the results but the patterns are designed to make it easy to see that they are correct. The code does not check any of the lseek() calls; that's a minor risk.



Related Topics



Leave a reply



Submit