How to Write to Middle of a File in C++

How to write to middle of a file in C++?

You cannot insert in the middle of the file. You have to copy the old file to a new file and insert whatever you want in the middle during copying to the new file.

Otherwise, if you intend to overwrite data/lines in the existing file, that is possible by using std::ostream::seekp() to identify the position within the file.

How do I insert and delete some characters in the middle of a file?

There is no simple method. You have do it manually. For example:

  1. Read the chunk you want to insert before into memory
  2. Seek forward to new position
  3. Write the chunk you just read at new position
  4. Seek back to where you want to insert
  5. Write the new data.

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.

C function to insert text at particular location in file without over-writing the existing text

No, there's no way to insert characters into an existing file. You will need to use a second file to do that.

Reading from and writing to the middle of a binary file in C/C++

You know the size of a float is sizeof(float), so multiplication can get you to the correct position:

FILE *f = fopen(fileName, "rb");
fseek(f, idx * sizeof(float), SEEK_SET);
float result;
fread(&result, sizeof(float), 1, f);

Similarly, you can write to a specific position using this method.

editing a specific line of a file in C

For fopen modes see http://www.cplusplus.com/reference/cstdio/fopen/. I think you need to use the option r+ because you are modifying the file in a random-access way fior both read and write.

"r+" read/update: Open a file for update (both for input and output).
The file must exist.

"w+" write/update: Create an empty file and open
it for update (both for input and output). If a file with the same
name already exists its contents are discarded and the file is treated
as a new empty file.

"a+" append/update: Open a file for update (both
for input and output) with all output operations writing data at the
end of the file. Repositioning operations (fseek, fsetpos, rewind)
affects the next input operations, but output operations move the
position back to the end of file. The file is created if it does not
exist.

I would suggest storing the number of lines in the file as an unsigned integer and not a string. The reason is that as a string 0-9 lines take one byte, but the minute you have 10 lines you need two byes, 100, 3 bytes and so on. In each case when an extra character is required you would have to re-write the entire file. I presume this is why you check that number of scores is less than 10.

A better solution would be to keep the first 4 bytes of the file as an unsigned integer and then start the ascii text after.

int      result;
uint32_t number_of_scores;
size_t bytesRead;
FILE *data;

...

/* Open a file for update (both for input and output).
* The file must exist. */
data = fopen("highscore.txt","r+");
if( !data )
exit(SOME_ERROR_CODE);

/* Read a 32-bit unsigned integer from the file. NOTE there is no endianess
* "protection" here... ignoring this issue for the sake of simplicity and relevance */
bytesRead = fread (&number_of_scores, sizeof(number_of_scores), 1, data);
if( bytesRead != 1 )
exit(SOME_ERROR_CODE);

/* Seek to end of file */
result = fseek(data, 0, SEEK_END);
if( result )
exit(SOME_ERROR_CODE);

/* Write in the next line */
result = fprintf(data,
"%s %s %s %s\n",
user[current_user].name,
user[current_user].age,
user[current_user].college,
resultVariableRenamedToAvoidNameCollision);

/* Up the number of scores and write it back to the start of the file */
number_of_scores++;
result = fseek(data, 0, SEEK_SET);
if( result )
exit(SOME_ERROR_CODE);

bytesRead = fwrite (data, sizeof(number_of_scores), 1, data);
if( bytesRead != 1 )
exit(SOME_ERROR_CODE);

fclose(data);

Doh, and I've just realised how late this answer is... never mind :S

How do I insert and delete some characters in the middle of a file?

There is no simple method. You have do it manually. For example:

  1. Read the chunk you want to insert before into memory
  2. Seek forward to new position
  3. Write the chunk you just read at new position
  4. Seek back to where you want to insert
  5. Write the new data.

Can fseek() be used to insert data into the middle of a file? - C

Yes it lets you do that, and those files are called "Random Access Files". Imagine you have already a set file ( with the structure but empty ), in that case you can fill the "slots" you want, or in the case the slot is filled with data you can overwrite on it.

typedef struct{
int number;
char name[ 20 ];
char lastname[ 20 ];
float score;
}students_t;

/* Supposing that you formatted the file already and the file is opened. */
/* Imagine the students are listed each one has a record. */

void modifyScore( FILE * fPtr ){
students_t student = { 0, "", "", 0.0 };
int nrecord;
float nscore;

printf( "Enter the number of the student:" );
scanf( "%d", &record )
printf( "Enter the new Score:" );
scanf( "%f", &nscore ); // this is a seek example so I will not complicate things.

/*Seek the file ( record - 1 ), because the file starts in position 0 but the list starts in 1*/
fseek( fPtr, ( record - 1 ) * sizeof ( students_t ), SEEK_SET );

/* Now you can read and copy the slot */
fread( fPtr, "%d%s%s%f", &student.number, student.name, student.lastname, &student.score );

/* Seek again cause the pointer moved. */
fseek( fPtr, ( record - 1 ) * sizeof ( students_t ), SEEK_SET );
student.score = nscore;

/*Overwrite his information, only the score will be altered. */
fwrite( &student, sizeof( student_t ), 1, fPtr );
}

This is how it works (picture obtained from Deitel-How to program in C 6th Edition):

Deitel-How to program in C 6th Edition



Related Topics



Leave a reply



Submit