0

I'm trying to build a gym program for a project in college. We're doing it in C in a Linux environment. I do not have problems reading from the file, but when I try to update the file, if I print to file with '\n' at the end, it puts double enters between line. And if I don't, it puts all the data in one line.

What should I do?

i.e. I've added an example of a function that reads from the file and one that updates it.

Employees** Init_Gym_emp(Employees** emp, int* num) {
    FILE* f = fopen("Gym Employees.txt", "r");
    if (f == NULL) {
        printf("Failed opening the file. Exiting!\n");
        exit(1);
    }
    char c = '\0';
    while (fscanf(f, "%c", &c) == 1) {
        if (c == '\n') num[0]++;
    }
    if (num[0] > 0) num[0]++;
    fseek(f, 0, SEEK_SET);
    Employees* tmp = (Employees*)malloc(sizeof(Employees));
    if (tmp == NULL) {
        printf("Memory allocation failed\n");
        exit(1);
    }
    emp = (Employees**)malloc(sizeof(Employees*)*(num[0]));
    if (emp == NULL) {
        printf("Memory allocation failed\n");
        exit(1);
    }
    int i = 0;
    tmp->first_name = (char*)malloc(sizeof(char)* 20);
    tmp->last_name = (char*)malloc(sizeof(char)* 20);
    tmp->user_name = (char*)malloc(sizeof(char)* 20);
    tmp->password = (char*)malloc(sizeof(char)* 20);
    tmp->user_type = (char*)malloc(sizeof(char)* 20);
    while (fscanf(f, "%20[^#]%*c%20[^#]%*c%ld%*c%20[^#]%*c%10[^#]%*c%20[^#]%*2c", tmp->first_name, tmp->last_name, &tmp->id, tmp->user_name, tmp->password, tmp->user_type) == 6) {
        emp[i] = (Employees*)malloc(sizeof(Employees));
        if (emp[i] == NULL) {
            printf("Memory allocation failed\n");
            exit(1);
        }
        emp[i]->first_name = (char*)malloc(sizeof(char)* (strlen(tmp->first_name) + 1));
        emp[i]->last_name = (char*)malloc(sizeof(char)* (strlen(tmp->last_name) + 1));
        emp[i]->user_name = (char*)malloc(sizeof(char)* (strlen(tmp->user_name) + 1));
        emp[i]->password = (char*)malloc(sizeof(char)* (strlen(tmp->password) + 1));
        emp[i]->user_type = (char*)malloc(sizeof(char)* (strlen(tmp->user_type) + 1));
        strcpy(emp[i]->first_name, tmp->first_name);
        strcpy(emp[i]->last_name, tmp->last_name);
        strcpy(emp[i]->user_name, tmp->user_name);
        strcpy(emp[i]->password, tmp->password);
        strcpy(emp[i]->user_type, tmp->user_type);
        emp[i]->id = tmp->id;
        i++;
    }
    free(tmp->first_name);
    free(tmp->last_name);
    free(tmp->user_name);
    free(tmp->password);
    free(tmp->user_type);
    free(tmp);
    fclose(f);
    return emp;
}


void update_Gym_emp(Employees** emp, int* num) {
    remove("Gym Employees.txt");
    FILE* f = fopen("Gym Employees.txt", "w");
    if (f == NULL) {
        printf("Failed opening the file. Exiting!\n");
        exit(1);
    }
    int i;
    for (i = 0; i < num[0]; i++) {
        fprintf(f, "%s#%s#%ld#%s#%s#%s#", emp[i]->first_name, emp[i]->last_name, emp[i]->id, emp[i]->user_name, emp[i]->password, emp[i]->user_type);
    }
    fclose(f);
}
donjuedo
  • 2,475
  • 18
  • 28
  • Note: they say [you shouldn't cast the result of `malloc()` in C](http://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc), – MikeCAT Dec 21 '15 at 13:35
  • 2
    Please read [How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve). It helps if people can compile the code and reproduce the problem. – JJF Dec 21 '15 at 13:36
  • 1
    Why don't you declare `Employees`'s members as simple arrays of `char` if you are willing to handle only strings which are less than 20 characters? – MikeCAT Dec 21 '15 at 13:37
  • 1
    First `sizeof(char)` is `1` by definition. Second. if available you can use `strdup()` instead of `malloc()`/`strcpy()`. – Andrew Henle Dec 21 '15 at 13:42
  • the files have # between each data and in the end of the line – user4949421 Dec 21 '15 at 14:10
  • and has i know and did before %*2c work's fine – user4949421 Dec 21 '15 at 14:11
  • i don't have a problem with reading from the file i have problem with writing to it – user4949421 Dec 21 '15 at 14:13
  • I tried, it doesn’t help, it's worse with it, the software breaks when it's getting to the update function – user4949421 Dec 21 '15 at 15:32
  • When calling `malloc()` and family of functions, 1) do not cast the returned value. it is already a `void*` so can be assigned to any other pointer. All the cast does is clutter the code and help to introduce errors when debugging and/or maintaining the code. 2) always check (!=NULL) the returned value to assure the operation was successful. – user3629249 Dec 22 '15 at 00:03
  • always check the returned value from a call to `fseek()` to assure the operation was successful – user3629249 Dec 22 '15 at 00:14
  • there is no need to call `remove()` because the following call to `fopen()` will truncate the file – user3629249 Dec 22 '15 at 01:04
  • always check the returned value from a call to `remove()` to assure the operation was successful – user3629249 Dec 22 '15 at 01:36
  • You've gone all the trouble to check the result of malloc but used the memory right after a malloc call in your while loop. Anyway, I'd create a macro that tests the pointer for null and does action if null. – hookenz Dec 22 '15 at 02:00

1 Answers1

0

here is one way the code could be written.

Notice that the first function reads from a file and creates a 2D list of Employee records

Notice that the second function writes to that same file from the 2D list of Employee records

Notice the string lengths for the call to fscanf() are one less than the related input buffer size because when scanning a string, the function will always append a NUL byte to the string

Strongly suggest defining the Employee struct as follows rather than the way it is currently defined:

struct Employee
{
    char first_name[20];
    char last_name[20];
    long id;
    char user_name[20];
    char password[10];
    char user_type[20];
};

Now, the proposed code

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

struct Employee
{
    char *first_name;
    char *last_name;
    long  id;
    char *user_name;
    char *password;
    char *user_type;
};

typedef struct Employee Employees;

// prototypes
void cleanup( Employees **emp );

Employees** Init_Gym_emp(Employees** emp, int* num)
{
    FILE* f = fopen("Gym Employees.txt", "r");
    if (f == NULL)
    {
        perror("fopen for Gym Employees.txt for input failed");
        exit(1);
    }

    // implied else, fopen successful

    char buffer[1024]; // input work area

    // get count of records in file
    while ( fgets(buffer, sizeof(buffer), f) )
    {
        num[0]++;
    }

    // step back to beginning of file to read/parse each record
    if( fseek(f, 0, SEEK_SET) )
    { // then fseek failed
         perror( "fseek to start of file failed" );
         fclose( f );
         exit( EXIT_FAILURE );
    }

    // implied else, fseek successful

    // allocate array of pointers large enough for all records in input file
    if( NULL == (*emp = malloc((size_t)num[0] * sizeof(Employees*) ) ) )
    {
        perror("malloc for array of pointers to Employee records failed");
        cleanup( emp );
        fclose( f );
        exit(1);
    }

    // clear all pointers to NULL, to make cleanup easier
    memset( *emp, '\0', (size_t)num[0] * sizeof( Employees* ));

    char first_name[20];
    char last_name[20];
    long id;
    char user_name[20];
    char password[10];
    char user_type[20];

    int i = 0;
    while( i < num[0] && fgets( buffer, sizeof(buffer), f ) )
    {
        if( 6 != sscanf(buffer,
                        "%19[^#]# %19[^#]# %ld# %19[^#]# %9[^#]# %19[^#]",
                        first_name,
                        last_name,
                        &id,
                        user_name,
                        password,
                        user_type) )
        { // then sscanf failed
            perror( "sscanf for fields of emp record failed" );
            cleanup( emp );
            fclose( f );
            exit( EXIT_FAILURE );
        }

        // implied else, sscanf successful

        // get room for one employee record
        if( NULL == (emp[i] = malloc(sizeof(Employees)) ) )
        {
            perror("malloc for new employee record failed");
            cleanup( emp );
            fclose( f );
            exit( EXIT_FAILURE );
        }

        // implied else, malloc successful

        (*emp)[i].first_name = strdup( first_name );
        (*emp)[i].last_name  = strdup( last_name );
        (*emp)[i].user_name  = strdup( user_name );
        (*emp)[i].password   = strdup( password );
        (*emp)[i].user_type  = strdup( user_type );
        (*emp)[i].id         = id;
        i++;
    } // end while


    fclose(f);
    return emp;
} // end function: Init_Gym_emp


void update_Gym_emp(Employees** emp, int* num)
{
    FILE* f = fopen("Gym Employees.txt", "w");
    if (f == NULL)
    {
        perror("fopen for Gym Employees.txt for write failed");
        cleanup( emp );
        exit(1);
    }

    // implied else, fopen successful

    for (int i = 0; i < num[0]; i++)
    {
        fprintf(f, "%s#%s#%ld#%s#%s#%s#",
                (*emp)[i].first_name,
                (*emp)[i].last_name,
                (*emp)[i].id,
                (*emp)[i].user_name,
                (*emp)[i].password,
                (*emp)[i].user_type);
    }
    fclose(f);
} // end function: update_Gym_emp
user3629249
  • 16,402
  • 1
  • 16
  • 17