Search code examples
cfilebinaryfiles

Reading a certain position in a binary file using structs with pointers


I am storing in a binary file a struct like this:

typedef struct user {
    char nick[6];
    int n_following;
    following *following;
}user;

That holds an array of structs of this type:

typedef struct following{
    char nick[6];
    int last_message;
}following;

I want to create and store in a binary file various users, and each time i do that i store their position in the file in a hashtable for later being able to seek them using fseek(), here is the aspect of an item in the hashtable:

typedef struct user_pos {
    char nick[6];
    int position_in_file;
}user_pos;

i tried to implement the ideia above and create two users and later try to fetch them, but i can only get one, since the other breaks the program.

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

typedef struct following{
    char nick[6];
    int last_message;
}following;

typedef struct user {
    char nick[6];
    int n_following;
    following *following;
}user;    

void insert_user(char *input_a, int n){

    FILE *p;
    p=fopen("users","ab");
    user *new_user = malloc(sizeof(user));
    strcpy(new_user->nick, input_a);
    new_user->n_following = n;
    new_user->following = malloc(sizeof(following) * new_user->n_following);
    strcpy(new_user->following[0].nick, "la");
    new_user->following[0].last_message = 0;
    strcpy(new_user->following[1].nick, "zz");
    new_user->following[1].last_message = 2;

    fwrite(&new_user->nick, sizeof(new_user->nick), 1, p);
    fwrite(&new_user->n_following, sizeof(new_user->n_following), 1, p);
    fwrite(new_user->following, sizeof(following), new_user->n_following, p);
    printf("user created\n");
    fclose(p);
}

void get_user_info(char *input_a, int pos){


    FILE *p;
    p=fopen("users","rb");
    user *print_user = malloc(sizeof(user));

    fseek(p, pos* sizeof(user), SEEK_SET);

    fread(&print_user->nick, sizeof(print_user->nick), 1, p);
    fread(&print_user->n_following, sizeof(print_user->n_following), 1, p);
    print_user->following = malloc(sizeof(following));
    fread(print_user->following, sizeof(following), 2, p);

    printf("nick: %s, following: %d\n", print_user->nick, print_user->n_following);
    for(int i = 0; i < print_user->n_following; i++){
        printf("nick: %s, last_read: %d\n", print_user->following[i].nick, print_user->following[i].last_message);
    }

    fclose(p);
}

int main(){
    //hashtable *active_users = create();
    char buffer[38];
    char tipo;
    int n;
    char input_a[6];
    while(fgets(buffer, 38, stdin)){
        sscanf(buffer, "%c %s %d", &tipo, input_a, &n);
        switch(tipo) {
            case 'U' :
                insert_user(input_a, n);
                break;
            case 'S' :
                get_user_info(input_a, n);
                break;
            case 'X' :
                exit(0);
            default :
                printf("Operacao invalida\n");
        }
    }
    return 0;
}

the program fails with this kind of input:

input: U me 2
output: user created
input: U ro 2
output: user created
input: S me 0
output: nick: me, following: 2
        nick: la, last_read: 0
        nick: zz, last_read: 2
input: S ro 1//program breaks

Why is the program above finding one of the users and not the other?

Note: i know this would be better to do with normal .txt files but i want to use the speed of the fseek() function to get the users in the file


Solution

  • You cannot call fseek(p, pos* sizeof(user), SEEK_SET); to skip to pos user in your file. sizeof(user) returns size in bytes of user struct but you have pointer following in this struct, which points to array filled by your following objects. If you want to skip to pos user you should have information how many following objects are in each user. You don't have this information so you can read your file user one by one to reach pos user.

    // pseudocode 
    get_user_info (int pos) {
       while (pos--) {
          skip 6 bytes // nick
          read 4 bytes n_following;
          skip n_following * sizeof(following) bytes
       }
       // here you can read data of pos user
    }