problems with writing a file in C in a Linux environment - c

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);
}

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

Related

How to assign a value to an array of structures in C?

There is a function load that reads an array of structures from a binary file and returns this array.It works correctly.But the problem is that I can not assign an array of structures the value that the load function returns.
struct tickets
{
char plane [7];
char zona [13];
int rate;
int cost;
};
struct tickets* load(char * filename)
{
FILE * file;
char *symbol;
int m = sizeof(int);
int n, i;
int *pti = (int *)malloc(m);
if ((file = fopen(filename, "r")) == NULL)
{
perror("Error occured while opening file");
}
symbol = (char *)pti;
while (m>0)
{
i = getc(file);
if (i == EOF) break;
*symbol = i;
symbol++;
m--;
}
n = *pti;
struct tickets* ptr = (struct tickets *) malloc(n * sizeof(struct tickets));
symbol = (char *)ptr;
while ((i= getc(file))!=EOF)
{
*symbol = i;
symbol++;
}
fclose(file);
return ptr;
}
int main(void)
{
char * filename = "p.dat";
struct tickets* ticket = NULL;
ticket = load(filename);
}
the following proposed code:
cleanly compiles
performs the desired functionality
properly checks for errors
informs the main() function as to how many instances of struct tickets were read
informs the user when any error occurs
cleans up after itself
eliminates one of the calls to malloc()
uses the function: fread() to avoid problems with little/big Endian
documents why each header file is included
assumes the ticket count (first 4 bytes of file) is in binary
incorporates appropriate horizontal and vertical spacing for readability
caveat: since no example input file posted, this code is not tested
and now, the proposed code:
#include <stdio.h> // perror(), NULL, FILE*, EOF,
// fopen(), fclose(), fread()
#include <stdlib.h> // malloc(), free(), exit(), EXIT_FAILURE
#define MAX_PLANE_LEN 7
#define MAX_ZONA_LEN 13
#define COUNT_LEN 4
struct tickets
{
char plane [ MAX_PLANE_LEN ];
char zona [ MAX_ZONA_LEN ];
int rate;
int cost;
};
struct tickets* load( char * filename, size_t * ticketCount )
{
FILE * fp;
if ( ! (fp = fopen( filename, "r" )) )
{
perror( "Error occured while opening file" );
exit( EXIT_FAILURE );
}
size_t bytesRead = fread( ticketCount, 1, COUNT_LEN, fp );
if( bytesRead != COUNT_LEN )
{
fprintf( stderr, "failed to read count of number of instances of 'struct tickets'\n" );
fclose( fp );
exit( EXIT_FAILURE );
}
struct tickets* ptr = malloc( *ticketCount * sizeof( struct tickets ) );
if( ! ptr )
{
perror( "malloc failed" );
fclose( fp );
exit( EXIT_FAILURE );
}
for( size_t i = 0; i < *ticketCount; i++ )
{
size_t numBytes = fread( &(ptr[i]), 1, sizeof( struct tickets ), fp );
if( numBytes != sizeof( struct tickets ) )
{
perror( "read of a struct ticket failed" );
fclose( fp );
free( ptr );
exit( EXIT_FAILURE );
}
}
fclose( fp );
return ptr;
}
int main( void )
{
char * filename = "p.dat";
size_t ticketCount = 0;
struct tickets* ticket = load( filename, &ticketCount );
// ...
free( ticket );
}

Read character by character from a file and put each line in a String

I've to read character by character from a file and put each line in a String.
The problem is that i don't know the size of each line so eventually I've to reallocate the memory. So If I try a reallocation my program return error. Am I doing something wrong?
FILE * file = fopen(input,"r");
if(file != NULL){
char temp;
char * line;
line = (char *) malloc(sizeof(char) * 10);
int i = 0;
while((temp = fgetc(file)) != EOF){
if(temp == '\n'){
i = 0;
}
else{
if(i > strlen(line) - 2){
line = (char *) realloc(line,sizeof(line) * 10);
}
line[i] = (char) temp;
i++;
}
}
free(line);
fclose(file);
}
else{
}
the following proposed code:
cleanly compiles
performs the desired functionality
properly checks for errors
outputs user error messages to stderr
outputs the text reason the system thinks an error occurred to stderr
documents why each header file is included
shows an example of how to handle the case where the user failed to enter a command line parameter (in this case a input file name)
makes use of size_t rather than int when passing parameters to malloc() and realloc()
and now, the proposed code:
#include <stdio.h> // fopen(), perror(), fclose() fprintf()
#include <stdlib.h> // exit(), EXIT_FAILURE, malloc(), realloc(). free()
int main( int argc, char *argv[] )
{
if( argc != 2 )
{
fprintf( stderr, "USAGE: %s <fileName>\n", argv[0] );
exit( EXIT_FAILURE );
}
FILE * file = fopen( argv[1], "r" );
if( !file )
{
perror( "fopen failed" );
exit( EXIT_FAILURE );
}
// implied else, fopen successful
int ch;
char * line = malloc( 10 );
if( !line )
{
perror( "malloc failed" );
fclose( file ); // cleanup
exit( EXIT_FAILURE );
}
// implied else, malloc successful
size_t lineLen = 10;
size_t i = 0;
while( (ch = fgetc(file)) != EOF )
{
if( ch == '\n' )
{
line[i] = '\0';
// do something with contents of line
i = 0;
}
else
{
if(i >= lineLen )
{
lineLen *= 2;
char * temp = realloc( line, lineLen );
if( !temp )
{
perror( "realloc failed" );
// cleanup
fclose( file );
free( line );
exit( EXIT_FAILURE );
}
line = temp;
}
line[i] = (char)ch;
i++;
}
}
free(line);
fclose(file);
}

Problems with malloc and realloc

Why do I get 0 bytes after a block of size 7 alloc'd error when I am still leaving space for '\0'?
I tried allocating and reallocating 7 bytes and kept the size variable going up by 5 so that there would always be at least 2 bytes left at the end when I add the null terminator, but I am still getting the valgrind error:
Invalid write of size 1:
0 bytes after a block of size 7 alloc'd
whenever I read or write to token, for example I get it on this line:
token[i] = read;
void parse_file(char file[]) {
char read = 0;
int size = 5;
char *token = NULL;
int i = 0;
FILE *fp = NULL;
token = malloc(7 * sizeof(char));
fp = fopen(file, "r");
if(fp == NULL) {
fprintf(stderr, "%s: No such file or directory\n", file);
free(token);
fclose(fp);
return;
}
read = fgetc(fp);
while(read != EOF) {
if(i == size) {
token = realloc(token, 7 * sizeof(char));
size += 5;
}
if(isalpha(read)) {
read = (char) tolower(read);
token[i] = read;
}
else {
if(isalpha(token[0])) {
token[i] = '\0';
put(token);
}
else {
free(token);
}
token = calloc(7,sizeof(char));
size = 5;
i = 0;
read = fgetc(fp);
continue;
}
read = fgetc(fp);
i++;
}
free(token);
fclose(fp);
}
the following proposed code:
cleanly compiles
eliminates unnecessary code/logic
performs the desired functionality
properly checks for errors
incorporates the comments to the OPs question
incorporates the comments to this answer
and now the proposed code: (EDITED)
#include <stdlib.h> // exit(), EXIT_FAILURE, realloc(), free()
#include <stdio.h> // FILE, fprintf(), fopen(), fgetc(), perror()
#include <ctype.h> // isalpha(), tolower()
#include <errno.h> // errno
#include <string.h> // strerror()
// prototypes
void parse_file(char fileName[]);
void parse_file(char fileName[])
{
int byteRead = 0;
size_t size = 0;
char *token = NULL;
size_t i = 0;
FILE *fp = NULL;
fp = fopen(fileName, "r");
if( !fp )
{
fprintf(stderr, "Can't open %s: %s\n", fileName, strerror(errno));
exit( EXIT_FAILURE );
}
while( (byteRead = fgetc(fp) ) != EOF )
{
char *temp = NULL;
if(i >= size)
{
temp = realloc(token, 7 + size );
if( !temp )
{
perror( "realloc failed" );
free( token );
fclose( fp );
exit( EXIT_FAILURE );
}
// implied else, realloc successful
size += 7;
token = temp;
}
if( isalpha(byteRead) )
{
byteRead = tolower(byteRead);
token[i] = (char)byteRead;
i++;
}
else if( i )
{
token[i] = '\0';
puts(token);
free( token );
i = 0;
size = 0;
}
}
free(token);
fclose(fp);
}

Sorting string from file

I want to sort strings from file; this code compiles well, but it stops working in line 29, when I do words_array[i] = strdup(line);.
From debugger I have "program received signal sigsegv segmentation fault"
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int comparator ( const void * elem1, const void * elem2 )
{
return strcmp( *(const char**) elem1, *(const char**) elem2);
}
int main()
{
char filename[]="dane.txt";
FILE* fp;
char* line = NULL;
size_t len = 0;
char** words_array = NULL;
int i = 0,j; // number of elements
// read list from file
if( ( fp = fopen(filename, "r") ) == NULL ) {
fprintf(stderr, "Cannot open source file %s!\n", filename);
exit(1);
}
for(; fgets(line, len, fp) != NULL; ++i) {
// put word in array
words_array = realloc(words_array, sizeof(char*) * (i + 1) );
words_array[i] = strdup(line);
}
fclose(fp);
free(line);
// sort it
qsort(words_array, i, sizeof(char*), comparator);
if( ( fp = fopen(filename, "a+") ) == NULL ) {
fprintf(stderr, "Cannot open source file %s!\n", filename);
exit(1);
}
// write to file and free dynamically allocated memory
for(j = 0; j < i; ++j) {
fprintf(fp, "%s", words_array[j]);
free(words_array[j]);
}
free(words_array);
fclose(fp);
return 0;
}
You never allocated space for line to point to.

Loading data from a file to an array in c

I want to load an array from a file that I previously saved. I ran the code below, but for some reason, when I loaded the array, the array length that I loaded was not the same as the length of the array that I had saved. How would I change the length for my load a file code so that it would work for any array length?
intarr_t* intarr_load_binary( const char* filename )
{
unsigned int len = 0;
FILE *f = fopen (filename, "rb");
fscanf (f, "%d", len);
intarr_t* newia = malloc (sizeof(intarr_t));
assert (newia);
newia->data = malloc (len*sizeof(int));
assert(newia->data);
newia->len = len;
if (f != NULL)
{
while (!feof(f))
{
fscanf (f, "%d", newia->data);
}
}
else
{
return NULL;
}
fclose (f);
return newia;
}
The struct that I used for the save/load is here:
typedef struct {
int* data;
unsigned int len;
} intarr_t;
The code that I used to save the file is here:
int intarr_save_binary( intarr_t* ia, const char* filename )
{
unsigned int len = ia->len;
FILE *f;
f = fopen (filename, "wb");
if (fwrite (ia->data, sizeof(int), len, f) == len)
{
return 0;
}
else
{
return 1;
}
fclose (f);
}
the code is writing no length (len) value as the first data item to the file
yet the code is reading a length (len) value
as if it were the first data item in the file.
this code is full of errors and oversights:
int intarr_save_binary( intarr_t* ia, const char* filename )
{
unsigned int len = ia->len;
FILE *f;
f = fopen (filename, "wb");
if (fwrite (ia->data, sizeof(int), len, f) == len)
{
return 0;
}
else
{
return 1;
}
fclose (f);
}
suggest using code similar to this:
int intarr_save_binary( intarr_t* ia, const char* filename )
{
int returnValue = 0;
unsigned int len = ia->len;
FILE *f;
if( NULL == (f = fopen (filename, "wb") )
{
perror( "fopen failed" );
returnValue = 1;
}
else if ( fwrite ( &len, sizeof(int), 1, f) == 1 )
{ // then write of length successful
if (fwrite (ia->data, sizeof(int), len, f) == len)
{
returnValue = 0; // indicate success
}
else
{ // else, write of data failed
returnValue = 3;
}
}
else
{ // else, failed to write len value to file
returnValue = 4;
}
fclose( f ); // cleanup (writes last buffer to file)
return( returnValue );
} // end function: intarr_save_binary
this code needs some work.
for instance because assert should not enabled in production code
and error conditions not being checked
and cleanup not being properly performed
if the program is ok to continue after an I/O error
by always returning NULL
then you could change the following,
to return NULL rather than exiting the program
intarr_t* intarr_load_binary( const char* filename )
{
unsigned int len = 0;
FILE *f = fopen (filename, "rb");
fscanf (f, "%d", len);
intarr_t* newia = malloc (sizeof(intarr_t));
assert (newia);
newia->data = malloc (len*sizeof(int));
assert(newia->data);
newia->len = len;
if (f != NULL)
{
while (!feof(f))
{
fscanf (f, "%d", newia->data);
}
}
else
{
return NULL;
}
fclose (f);
return newia;
}
suggest:
intarr_t* intarr_load_binary( const char* filename )
{
unsigned int len = 0;
FILE *f = NULL;
intarr_t* newia = NULL;
if( NULL == fopen (filename, "rb") )
{ // then, fopen failed
perror( "fopen failed" );
exit( EXIT_FAILURE );
} // end if
// implied else, fopen successful
if( NULL == (newia = malloc (sizeof(intarr_t)) )
{ // then malloc failed
perror( "malloc failed" );
fclose(f);
exit( EXIT_FAILURE );
} // end if
// implied else, malloc successful
if( (fread (&len, sizeof(int), 1, f) != 1 ) )
{ // then fread failed
perror( "fread failed" );
fclose(f);
free( newia );
exit( EXIT_FAILURE );
} // end if
// implied else, fread for len successful
newis->len = len;
if( NULL == (newia->data = malloc (len*sizeof(int)) ) )
{ // then malloc failed
perror( "malloc failed" );
fclose(f);
free( newia );
exit( EXIT_FAILURE );
} // end if
// implied else, malloc successful
if( fread( newia->data, sizeof(int), len, f ) != len )
{ // then, fread failed
perror( "fread failed" );
fclose(f);
free(newia->data)l
free(newia);
exit( EXIT_FAILURE );
} // end if
// implied else, fread successful
fclose (f);
return newia;
} // end function: intarr_load_binary

Resources