Tuesday, June 05, 2007

read/write the binary content to file

An example shown here to present
1) how to write the binary content to a file
2) and then read the content from it

Some steps
1) Prepare chunks of memory
Usually the content will be records of memory of any structure type, in case it can be restored back with the structure in step4.
2) Output to a file
Open a file first by open()/fopen(), and then write the records to the file by write()/fwrite(). Of course, all the content can be written once or one by one, it depends on your design decision. Remember to close the file at last.
3) Input the file
We want to read the binary content. First we should open the file and read the content by read()/fread(). How many size we need? Actually, we would not have no idea the real amount of record, but we know the basic element should be the original structure type. so we can use the structure as the element size and then get the chunks of the same size one by one. Finally and without any exception, when we can get nothing, we have gotten all the records without any damage.
4) Recognize the content
The way to recognize the binary content is usually to cast the binary record to be of the original structure type.

Pitfall here,
Usually, we can get the exact all the records without any damage. But exceptions always happen around, so we should have some preventions in hand. The common exception here is the damaged data, so how to make sure the data integrity is the major task. A way to keep data integrity is append the checksum to the real data. so before storing the data, we calculating the checksum and append the result to the data, and after we read the content, we calculate the checksum and compare with the old one. If they match, the data isn't damaged. If not, trouble happened.

Another issue concerned here is the structure layout. Usually, the structure size will always be the multiples of 4, which is defined by the WORD size of the processor. If the actual size is not the multiples of 4, the compiler will expand it to be the multiples by stuffing the hole up. So make sure the structure as compact as possible.

Issue of binary data exchange between different byte order systems is also the problem. It should be the responsibility of the application of upper layer to make sure the portability.

#include <stdio.h>
#include <time.h>
#include <string.h>

#define USER_LEN 10
#define PASS_LEN 10
#define TOTAL_ACCOUNTS 10

typedef struct {
time_t create_time;
char user[USER_LEN+1];
char pass[PASS_LEN+1];
char reserved[2];
} account_t ;

int main(int argc, char *argv[])
{
int i=0;
FILE *fd;
account_t acc[TOTAL_ACCOUNTS];

// writer
memset(acc, 0x00, sizeof(acc[0])*TOTAL_ACCOUNTS);
for (i=0; i< TOTAL_ACCOUNTS; ++i) {
snprintf(acc[i].user, sizeof(acc[0].user)-1, "user%d", i);
snprintf(acc[i].pass, sizeof(acc[0].pass)-1, "pass%d", i);
time(&acc[i].create_time);
}
fd = fopen("accounts.bin", "wb");
fwrite(acc, sizeof(acc[0]), TOTAL_ACCOUNTS, fd);
fclose(fd);

// reader
memset(acc, 0x00, sizeof(acc[0])*TOTAL_ACCOUNTS);
fd = fopen("accounts.bin", "rb");
for (i=0; !feof(fd) ; ++i) {
fread(&acc[i], sizeof(acc[0]), 1, fd);
if ( feof(fd) ) // A pitfall here
break;

printf("%d,%s/%s/%s\n", i, acc[i].user, acc[i].pass
, ctime(&acc[i].create_time) );
}
fclose(fd);
return 0;
}


Usually, hexdump is alwasy useful to dump the binary content out.

No comments: