BRHSM
BRHSM

Reputation: 884

unable to read struct from a binary file using C when file is not 100% filled as expected

I have this file which i try to read the contacts from for a simple contactbook using a structure.

typedef struct contact{
    char name[80];
    char surname[80];
    char cellnumber[20];
    float length;
    int contactid;
}contact;

i use this function and I call it like this so it reads it 200 times (const int MAXCONTACT = 200;).

for(i2=0;i2<MAXCONTACT;i2++)
    person[i2]=load(i2);

This is the function that given a certain ID (the variabile i), returns the contact which has the same I:

contact load(int i){
    struct contact person;
    FILE *data;
    data=fopen("data.bin","rb");
    do{
        fread(&person,sizeof(contact),1,data);
    }while(person.contactid!=i);
    fclose(data);
    return person;
}

The only problem with this kind of code is, that when there are no 200 contacts, the function will not return a contact because the contact ID can't be found.

I had some solutions in mind but they are rather complex and i wanted to know if they could be done better.

  1. make an installer that creates the files just like a real installer would and then create 200 undeclared contacts with variabiles that are equal to null.

  2. check if the program is being ran for the first time and do the same thing as above only in the program itself.

  3. if the contact ID is not found exit the searching loop equally and return an undecleared contact with all variabiles set to null.

After writing this, three questions come to mind:

Which one of these would be the best one or the easiest to work with?

Is it safe to return an undecleared contact? (I have to keep in mind that I have to work with the contacts: modefy, print, print all contacts)

Should I have a structure and file to write down some statistics of variabiles that are not used in the contact structure/file?

Upvotes: 0

Views: 283

Answers (2)

Andrew Henle
Andrew Henle

Reputation: 1

Try this. It returns indication that the data read worked or failed, by returning 0 if it worked, -1 if not (note that I changed "i" to unsigned as there is no way to properly deal with a negative value):

int load(unsigned int i, struct contact *person)
{
    int data;
    struct stat sb;
    data=open("data.bin", O_RDONLY);
    if ( data == -1 ) return( -1 );
    /* use fstat() to tell how big a file is */
    fstat( data, &sb );
    /* sb.st_size now holds the number of bytes in the file
       it needs to be at least as big as ( i + 1 ) contacts */
    if ( sb.st_size < ( ( 1 + i ) * sizeof( *person ) ) )
    {
        close( data );
        return( -1 );
    }
    /* seek to record i */
    lseek( data, ( i * sizeof( *person ) ), SEEK_SET );
    ssize_t bytesRead = read( data, person, sizeof( *person ) );
    /* return -1 if the read didn't get enough bytes */
    if ( bytesRead != sizeof( *person ) )
    {
        close( data );
        return( -1 );
    }
    close(data);
    return( 0 );
}

That will allow the caller to know if the read succeeded.

You'd have to change your code to pass the address of the contact struct you want filled in, but you'll KNOW if the read worked.

Upvotes: 1

user2551017
user2551017

Reputation: 163

Try the following approach, linear complexity, if a contact is absent, it will be initialized to 0.

/* initialize memory */
memset(person, 0, MAXCONTACT * sizeof(contact0);

/* open the file */
data=fopen("data.bin","rb");

/* get the file size */
fseek(data, 0L, SEEK_END);

/* don't read more than your allocated array can contain */
int size = max(ftell(data), MAXCONTACT * sizeof(contact));

/* seek to the beginning of the file */
fseek(fp, 0L, SEEK_SET);

/* populate the array */
fread(&person,size,1,data);

/* close the file */
fclose(data);   

Upvotes: 1

Related Questions