Binary files, structs and fread

L

Luc Holland

Hey,

I'm working on a program that reads a binary file. It's opened with
====
if ((f1=fopen(argv[1],"rb"))==NULL) {
fprintf(stderr,"Error opening %s for reading . . .\n",argv[1]);
exit(2);
}
====
The structure of the file is:

unsigned int
unsigned short
unsigned short
unsigned short
unsigned short
unsigned short
<more data>
{eof}
I have a structure:

typedef struct struct_fileinfo {
unsigned int num;
unsigned short ins;
unsigned short del;
unsigned short nex;
unsigned short est;
unsigned short get;
} FILEINFO;

int main(int argc, char** argv) {
FILEINFO data;
FILE* f1;
[......]

I use:
if ((fread(&data,sizeof(FILEINFO),1,f1))!= 1) {
fprintf(stderr,"\n\t\tError reading into fileinfo\n\n");
exit(3);
}
to read a chunk the size of FILEINFO.

When I go to output the data read with

fprintf(stdout,"%hd",data.num);

I dont get a value I'm expecting. Am I using the proper printf
identifier (%hd for unsigned int and %hu for unsigned short)? Should i
be casting the values before output? Any suggestions? OS is Solris 8
on Enterprise 220R.

Thanks!
Luc
 
A

Al Bowers

Luc said:
Hey,

I'm working on a program that reads a binary file. It's opened with
====
if ((f1=fopen(argv[1],"rb"))==NULL) {
fprintf(stderr,"Error opening %s for reading . . .\n",argv[1]);
exit(2);
}
====
The structure of the file is:

unsigned int
unsigned short
unsigned short
unsigned short
unsigned short
unsigned short
<more data>
{eof}
I have a structure:

typedef struct struct_fileinfo {
unsigned int num;
unsigned short ins;
unsigned short del;
unsigned short nex;
unsigned short est;
unsigned short get;
} FILEINFO;

int main(int argc, char** argv) {
FILEINFO data;
FILE* f1;
[......]

I use:
if ((fread(&data,sizeof(FILEINFO),1,f1))!= 1) {
fprintf(stderr,"\n\t\tError reading into fileinfo\n\n");
exit(3);
}
to read a chunk the size of FILEINFO.

When I go to output the data read with

fprintf(stdout,"%hd",data.num);

data.num is type unsigned int. The specifier should be %u.
I dont get a value I'm expecting. Am I using the proper printf
identifier (%hd for unsigned int and %hu for unsigned short)? Should i
be casting the values before output? Any suggestions? OS is Solris 8
on Enterprise 220R.

You need to be aware that the sizeof the individual struct members may
not equate to the sizeof the struct. The struct may have padding
making it larger than the total members size. This would cause
an alignment problem with your code.
See the faq: http://www.eskimo.com/~scs/C-faq/q2.13.html

If you can't modify the data file, you could write a function
that will read the data file and store the data in a struct.

#include <stdio.h>
#include <stdlib.h>

/* data in data file
unsigned int
unsigned short
unsigned short
unsigned short
unsigned short
unsigned short
<more data>
{eof} */

typedef struct struct_fileinfo {
unsigned int num;
unsigned short ins;
unsigned short del;
unsigned short nex;
unsigned short est;
unsigned short get;
} FILEINFO;

int GetData(FILEINFO *p,const char *fname);

int main(void)
{
const char *fname = "test.dat";
unsigned short i;
unsigned int d = 99;
FILEINFO test = {0,0,0,0,0,0};
FILE *fp;

/* Create a test file */
if((fp = fopen(fname,"wb")) == NULL) exit(EXIT_FAILURE);
if(1 != fwrite(&d,sizeof(d),1,fp)) exit(EXIT_FAILURE);
for(i = 0; i < 5;i++)
if(1 != fwrite(&i,sizeof(i),1,fp)) exit(EXIT_FAILURE);
fclose(fp);

printf("The sizeof the indivual elements: %u\n",
sizeof(short)*5+sizeof(unsigned int));
printf("The sizeof of the struct is: %u\n",
sizeof(FILEINFO));

if(GetData(&test,fname))
printf("test.mum = %u\ntest.ins = %hd\n"
"test.del = %hd\ntest.nex = %hd\n"
"test.est = %hd\ntest.get = %hd\n",
test.num,test.ins,test.del,test.nex,
test.est,test.get);
return 0;
}

int GetData(FILEINFO *p,const char *fname)
{
FILE *fp;
int flag = 1;

if((fp = fopen(fname,"rb")) == NULL) return 0;
if(1 != fread(&p->num,sizeof(p->num),1,fp)) flag = 0;
if(1 != fread(&p->ins,sizeof(p->ins),1,fp)) flag = 0;
if(1 != fread(&p->del,sizeof(p->del),1,fp)) flag = 0;
if(1 != fread(&p->nex,sizeof(p->nex),1,fp)) flag = 0;
if(1 != fread(&p->est,sizeof(p->est),1,fp)) flag = 0;
if(1 != fread(&p->get,sizeof(p->get),1,fp)) flag = 0;
fclose(fp);
return flag;
}
 
M

Michael B Allen

You need to be aware that the sizeof the individual struct members may
not equate to the sizeof the struct. The struct may have padding making
it larger than the total members size. This would cause an alignment
problem with your code.
See the faq: http://www.eskimo.com/~scs/C-faq/q2.13.html

If you can't modify the data file, you could write a function that will
read the data file and store the data in a struct.
int GetData(FILEINFO *p,const char *fname) {
FILE *fp;
int flag = 1;

if((fp = fopen(fname,"rb")) == NULL) return 0; if(1 !=
fread(&p->num,sizeof(p->num),1,fp)) flag = 0; if(1 !=
fread(&p->ins,sizeof(p->ins),1,fp)) flag = 0; if(1 !=
fread(&p->del,sizeof(p->del),1,fp)) flag = 0; if(1 !=
fread(&p->nex,sizeof(p->nex),1,fp)) flag = 0; if(1 !=
fread(&p->est,sizeof(p->est),1,fp)) flag = 0; if(1 !=
fread(&p->get,sizeof(p->get),1,fp)) flag = 0; fclose(fp); return
flag;

There are two methods I advocate for reading and writing binary
formats. If the format does not need to be portable and it is generated
by your code or you know the code generating the format is just writing
the entire sizeof(struct foo) to the file then just read sizeof(struct
foo) into a 'struct foo' as well. But if fields have specific offsets
or if the file needs to be transported to another machine for decoding,
it is better to explicitly decode the format using endian-aware functions.
My encdec package provides the encoding/decoding primatives for complex
formats using the later technique:

http://www.ioplex.com/~miallen/encdec/

The tests/t3encdec.c test program illustrates how I would encode and
decode a struct like FILEINFO.

I do not advocate mixing the two techniques. If you're going to go
through the trouble of decoding each field, you might as well do it in
a portable way.

Mike
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top