Search code examples
cfilestructbinaryfilesfwrite

How can I write a struct from a binary file to a nested struct in another binary file?


I have this code. Let's say I want to link flight 1 (generated with function InputFlight) to User 1 (generated with function InputUser). Note: struct user has another nested struct called booking, that has a nested struct called flight.

By moving with pointers through binary files all I do is:

Ask user a flight number (let's suppose the first flight) -> So I move to the first flight.

Ask user which user he is (let's suppose the first user) -> So I move to the first user.

Then through fread() and fwrite(), I try to copy what's inside the struct flight into travelers.b.list, but somehow it won't work.

I already tried everything. I tried changing parameters in fwrite() and fseek() but without success. I guess the main problem is in the function checkin() inside booking.h, but I can't seem to solve it.

main.c

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

#include "flight.h"
#include "user.h"

void login_admin();

int main() {
    setbuf(stdout, NULL);

    login_admin();

return 0;
}

void login_admin() {

    FILE *file;
    FILE *file1;

    unsigned short int userchoice1 = 0;
    unsigned short int s = 0;

    while (s != 1) {    
        printf("\n-------------- M E N U --------------");
        printf("\n----- A D M I N I S T R A T O R -----");
        printf("\n1) Insert user");
        printf("\n2) Insert flight");
        printf("\n3) Check-in");
        printf("\n9) Exit program");
        printf("\n-------------------------------------");
        printf("\nYOUR CHOICE: ");
        scanf("%hu", &userchoice1);

        if (s != getchar()) {
        }
        switch (userchoice1) {
        case 1:
            inputUser(file);
            break;
        case 2:
            inputFlight(file);
            break;
        case 3:
            checkin(file, file1);
            break;
        case 9:
            s = 1;
            exit(0);
            break;
        default:
            printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
            printf("\nValue not valid.");
            break;
        }
    }
}

booking.h

#include <stdio.h>

 typedef struct {
    flight list;
    char booking_code[MAX_BOOKING_CODE];
} booking; //struct booking

 typedef struct {
    char name[MAX_NAME];
    char surname[MAX_SURNAME];
    date date_of_birth;
    char place_of_birth[MAX_PLACE_OF_BIRTH];
    char passport_number[MAX_PASSPORT];
    booking b;
 } user; //struct user

void checkin(FILE *flights, FILE *users) {

    flight list;
    user travelers;

    int user_number = 0;
    int choice = 0;
    int choice1= 0;

    if( (users = fopen("users.dat", "rb") ) == NULL) {
        printf("Error.(1)");
    }
    else
    {
        printf("\nWhat's your user number?");
        printf("\nUSER NUMBER: ");
        scanf("%d", &user_number);
        fseek(users, (user_number-1)*sizeof(user), SEEK_SET);
        fread(&travelers, 1, sizeof(user), users);

        if( (flights = fopen("flights.dat", "rb") ) == NULL) {
            printf("Error.");
        }
        else {
            printf("Available flights:\n\n");

                flights = fopen("flights.dat", "rb");

                while( !(feof(flights)))
                {

                int read = fread(&list, 1, sizeof(flight), flights);
                if ( read > 0) {
                    printf("Flight code: %s\nCompany name: %s\nDEPARTURE FROM: %s\nARRIVAL AT: %s\nPlane code: %s\nDay of flight: %hu\nMonth of flight: %hu\n"
                            "Year of flight: %hu\nHour of departure: %hu:%hu\nHour of arrival: %hu:%hu\nFlight time: %hu min.\n\n",
                            list.flight_code, list.companyname, list.departure,
                            list.arrival, list.plane_code, list.date_of_flight.day,
                            list.date_of_flight.month, list.date_of_flight.year, list.hour_departure,
                            list.minute_departure,list.hour_arrival,list.hour_departure, list.flight_time);
                }

            }

            printf("Which flight you want to choose?\n");
            printf("YOUR CHOICE: ");
            scanf("%d", &choice1);

            fseek(flights, (choice1-1)*sizeof(flight), SEEK_SET);
            fread(&list, 1, sizeof(flight), flights);

            printf("\n--------YOU CHOSE FLIGHT %d-------- \n\n", choice1);
                    printf("Flight code: %s\nCompany name: %s\nDEPARTURE FROM: %s\nARRIVAL AT: %s\nPlane code: %s\nDay of flight: %hu\nMonth of flight: %hu\n"
                            "Year of flight: %hu\nHour of departure: %hu:%hu\nHour of arrival: %hu:%hu\nFlight time: %hu min.\n\n",
                            list.flight_code, list.companyname, list.departure,
                            list.arrival, list.plane_code, list.date_of_flight.day,
                            list.date_of_flight.month, list.date_of_flight.year, list.hour_departure,
                            list.minute_departure,list.hour_arrival,list.hour_departure, list.flight_time);


            users = fopen("users.dat", "rb+");
            fseek(users, (user_number-1)*sizeof(user), SEEK_SET);

            fwrite(&travelers.b.list, 1, sizeof(user), users);

            fclose(users);
            fclose(flights);

flight.h

#include <stdio.h>
#include "define.h"

typedef struct {
    unsigned short int day;
    unsigned short int month;
    unsigned short int year;
 } date; //struct date

 typedef struct {
    char flight_code[MAX_FLIGHT_CODE];
    char companyname[MAX_COMPANY_NAME];
    char departure[MAX_DEPARTURE];
    char arrival[MAX_ARRIVAL];
    char plane_code[MAX_PLANE_CODE];
    unsigned short int seats[MAX_SEATS];
    date date_of_flight;
    unsigned short int hour_departure;
    unsigned short int minute_departure;
    unsigned short int hour_arrival;
    unsigned short int minute_arrival;
    unsigned short int flight_time;
} flight; //struct flight

void inputFlight(FILE *flight_file);

void inputFlight(FILE *flight_file) {

    flight list;

    static int i = 0;

    //for (int i = 0; i < 20; i++) {

        flight_file = fopen("flights.dat","wb");
        printf("Flight code %d: ", i+1);
        scanf("%6s", list.flight_code);
        fflush(stdin);
        printf("Company name of flight %d: ", i+1);
        scanf("%19s", list.companyname);
        fflush(stdin);
        printf("DEPARTURE: ");
        scanf("%19s", list.departure);
        fflush(stdin);
        printf("ARRIVAL: ");
        scanf("%19s", list.arrival);
        fflush(stdin);
        printf("Plane code of flight %d: ", i+1);
        scanf("%4s", list.plane_code);
        fflush(stdin);
        printf("Day of flight %d: ", i+1);
        scanf("%hu", &list.date_of_flight.day);
        fflush(stdin);
        printf("Month of flight %d: ", i+1);
        scanf("%hu", &list.date_of_flight.month);
        fflush(stdin);
        printf("Year of flight %d: ", i+1);
        scanf("%hu", &list.date_of_flight.year);
        fflush(stdin);
        printf("Hour and minutes of departure flight %d (separated by spacebar): ", i+1);
        scanf("%hu%hu", &list.hour_departure, &list.minute_departure);
        fflush(stdin);
        printf("Hour and minutes of arrival flight %d (separated by spacebar): ", i+1);
        scanf("%hu%hu", &list.hour_arrival, &list.minute_arrival);
        fflush(stdin);
        printf("Flight time: ");
        scanf("%hu", &list.flight_time);
        fflush(stdin);

        fwrite(&list, 1, sizeof(flight), flight_file);

        fclose(flight_file);

    //}
}

user.h

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

void inputUser(FILE *input);

void inputUser(FILE *input) {


    if( (input = fopen("users.dat", "wb") ) == NULL) {
        printf("Error.");
    }
    else {

        user travelers;

        static int i = 0;

        //for (int p = 0; p < 3; p++) {

            printf("Name user %d: ", i+1);
            scanf("%19s", travelers.name);
            fflush(stdin);
            printf("Surname user %d: ", i+1);
            scanf("%19s", travelers.surname);
            fflush(stdin);
            printf("Day of birth user %d: ", i+1);
            scanf("%hu", &travelers.date_of_birth.day);
            fflush(stdin);
            printf("Month of birth user %d: ", i+1);
            scanf("%hu", &travelers.date_of_birth.month);
            fflush(stdin);
            printf("Year of birth user %d: ", i+1);
            scanf("%hu", &travelers.date_of_birth.year);
            fflush(stdin);
            printf("Place of birth user %d: ", i+1);
            scanf("%9s", travelers.place_of_birth);
            fflush(stdin);
            printf("Passport number user %d: ", i+1);
            scanf("%8s", travelers.passport_number);
            fflush(stdin);

            fwrite(&travelers, 1, sizeof(user), input);
            i++;

        //}

        fclose(input);
    }
}

define.h

#ifndef DEFINE_H_
#define DEFINE_H_

#define MAX_NAME 20
#define MAX_SURNAME 20
#define MAX_PASSPORT 9
#define MAX_PLACE_OF_BIRTH 15

#define MAX_FLIGHT_CODE 6
#define MAX_COMPANY_NAME 20
#define MAX_DEPARTURE 20
#define MAX_ARRIVAL 20
#define MAX_PLANE_CODE 5
#define MAX_SEATS 150

#define MAX_BOOKING_CODE 5

#endif /* DEFINE_H_ */

Expected results are that struct list, seeked (basically chosen) through fseek() in function checkin() should be saved inside the struct travelers.b.list, while all I get is a mix of various symbols etc when I try to printf it.

I hope I got everything clear for you and sorry in advance for the bad formatting etc.

This is a really big University project for me, I hope someone can help me out.

Thank you.


Solution

  • In inputUser you write doing

    fwrite(&travelers, 1, sizeof(user), input);
    

    it seems more logical to do

    fwrite(&travelers, sizeof(user), 1, input);
    

    because fwrite is

    size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);

    rather than

    size_t fwrite(const void *ptr, size_t nmemb, size_t size, FILE *stream);

    Of course it is the same elsewhere like in checkIn, and for the fread where you also exchange nmemb and size


    Note in checkin you do two times flights = fopen("flights.dat", "rb") :

        if( (flights = fopen("flights.dat", "rb") ) == NULL) {
            printf("Error.");
        }
        else {
            printf("Available flights:\n\n");
    
                flights = fopen("flights.dat", "rb");
    

    because you use the same variable flights only the second FILE can be closed. After a time you will not be able to open again a file because the number of open file at the same time is limited.


    Warning

     while( !(feof(flights)))
    

    generally does not work, use the result of fread to stop to read


    You can remove all your fflush(stdin);, they do nothing :

    For input streams associated with seekable files (e.g., disk files, but not pipes or terminals), fflush() discards any buffered data ...


    I encourage you to check the result of your scanf to be sure a valid input was enter, so to check it returns 1 when you read a value and 2 the few cases you read 2 values

    Also in

       scanf("%d", &user_number);
       fseek(users, (user_number-1)*sizeof(user), SEEK_SET);
       fread(&travelers, 1, sizeof(user), users);
    

    and

           scanf("%d", &choice1);
    
           fseek(flights, (choice1-1)*sizeof(flight), SEEK_SET);
           fread(&list, 1, sizeof(flight), flights);
    

    there is no check at all

    So you do not know what you read, you suppose a valid int was enter for user_number and choice1, you suppose their value is compatible with the size of the file, then you suppose you can read a user/fligh

    You really need to add checks


    I guess the main problem is in the function checkin() inside booking.h

    Yes, in checkin your way to modify the flight part of a user is not the right, you do

     fseek(users, (user_number-1)*sizeof(user), SEEK_SET);
    
     fwrite(&travelers.b.list, 1, sizeof(user), users);
    

    the fseek set the position at the beginning of the user in the file, but rather than to fwrite all the user you fwrite the sub part travelers.b.list so you replace name/surname/... by the flight (and the memory after it because you write the size of the user rather than on the the size of a flight) which furthermore is not initialized because previously you fread in the variable list rather than in travelers.b.list.

    A way to correct can be to update travelers.b.list then to write all the user, so to replace

           fread(&list, 1, sizeof(flight), flights);
    
           printf("\n--------YOU CHOSE FLIGHT %d-------- \n\n", choice1);
                   printf("Flight code: %s\nCompany name: %s\nDEPARTURE FROM: %s\nARRIVAL AT: %s\nPlane code: %s\nDay of flight: %hu\nMonth of flight: %hu\n"
                           "Year of flight: %hu\nHour of departure: %hu:%hu\nHour of arrival: %hu:%hu\nFlight time: %hu min.\n\n",
                           list.flight_code, list.companyname, list.departure,
                           list.arrival, list.plane_code, list.date_of_flight.day,
                           list.date_of_flight.month, list.date_of_flight.year, list.hour_departure,
                           list.minute_departure,list.hour_arrival,list.hour_departure, list.flight_time);
    
    
           users = fopen("users.dat", "rb+");
           fseek(users, (user_number-1)*sizeof(user), SEEK_SET);
    
           fwrite(&travelers.b.list, 1, sizeof(user), users);
    

    by

            fread(&travelers.b.list, sizeof(flight), 1, flights);
    
            printf("\n--------YOU CHOSE FLIGHT %d-------- \n\n", choice1);
                    printf("Flight code: %s\nCompany name: %s\nDEPARTURE FROM: %s\nARRIVAL AT: %s\nPlane code: %s\nDay of flight: %hu\nMonth of flight: %hu\n"
                            "Year of flight: %hu\nHour of departure: %hu:%hu\nHour of arrival: %hu:%hu\nFlight time: %hu min.\n\n",
                            travelers.b.list.flight_code, travelers.b.list.companyname, travelers.b.list.departure,
                            travelers.b.list.arrival, travelers.b.list.plane_code, travelers.b.list.date_of_flight.day,
                            travelers.b.list.date_of_flight.month, travelers.b.list.date_of_flight.year, travelers.b.list.hour_departure,
                            travelers.b.list.minute_departure,travelers.b.list.hour_arrival,travelers.b.list.hour_departure, travelers.b.list.flight_time);
    
    
            users = fopen("users.dat", "rb+");
            fseek(users, (user_number-1)*sizeof(user), SEEK_SET);
    
            fwrite(&travelers, sizeof(user), 1, users);
    

    An other way closer to your code is to fseek at the right position and to write the right flight with the right size, so to replace

     fseek(users, (user_number-1)*sizeof(user), SEEK_SET);
    
     fwrite(&travelers.b.list, 1, sizeof(user), users);
    

    by

     fseek(users, (user_number-1)*sizeof(user) + (((char *) &travelers.b.list) - ((char *) &travelers)), SEEK_SET);
    
     fwrite(&list, sizeof(list), 1, users);