Implementation of Dynamic memory allocation in files in C program - c

I want the program to read the details of n employees from the file and store the Salary details of n employees to an array which is dynamically allocated and then print the average salary.
The below program gets the data from the user and stores it in a file.
#include <stdio.h>
#include <stdlib.h>
typedef struct
{
char name[10];
char nameEr[10];
int salaryannual;
char desig[10];
}Employee;
void main(int argc, char *argv[])
{
int i,n=0,*ptr,sum=0;
n=atoi(argv[1]);
printf("The no.of employee records to be created are: %d\n",n);
FILE *fp;
if((fp=fopen("employee.txt","w"))==NULL)
{
printf("File could not be opened\n");
}
Employee employees[n];
printf("Enter %d Employee Details: \n \n",n);
for(i=0; i<n; i++)
{
printf("Employee %d: \n",i+1);
printf("Employee name: ");
scanf("%s",employees[i].name);
printf("Employer name: ");
scanf("%s",employees[i].nameEr);
printf("Employee designation: ");
scanf("%s",employees[i].desig);
printf("Employee annual salary: ");
scanf("%d",&employees[i].salaryannual);
printf("\n");
}
for(i=0;i<n;i++)
{
fprintf(fp,"%s\n",employees[i].name);
fprintf(fp,"%s\n",employees[i].nameEr);
fprintf(fp,"%s\n",employees[i].desig);
fprintf(fp,"%d\n",employees[i].salaryannual);
}
fclose(fp);
for(i=0;i<n;i++)
{
printf("Employee name \t: ");
printf("%s \n",employees[i].name);
printf("Employer name \t: ");
printf("%s \n",employees[i].nameEr);
printf("Employee designation \t: ");
printf("%s \n",employees[i].desig);
printf("Employee annual salary: ");
printf("%d \n",employees[i].salaryannual);
printf("\n");
}
How do I proceed further from this code.
Please help me.

There are certainly many approaches to solve this.
I think of one of the simplest is:
Get the employee count by opening the file for reading and counting the number of lines. The employee count will be the number of lines divided by four, since there are four lines for each employee. You typically make a function to do so:
int get_number_of_employees(int *number_of_employees)
{
int ret = -1;
int line_count = 0;
FILE *fp = NULL;
if ((fp = fopen("employee.txt", "r")) != NULL) {
char line[64] = {0,};
while(fgets(line, sizeof(line), fp)) {
line_count++;
}
*number_of_employees = line_count / 4;
ret = 0;
fclose(fp);
}
return ret;
}
Allocate memory for your buffer using the number of employees you got with the function above:
int salaries_array[employees_count];
Create a function fo fill in the array you just created. This will require the program to open the file for reading again:
int get_employees_salaries(int salaries_array[], int employees_count)
{
int ret = -1;
int line_count = 0;
FILE *fp = NULL;
if ((fp = fopen("employee.txt", "r")) != NULL) {
char line[64] = {0,};
while(fgets(line, sizeof(line), fp)) {
line_count++;
if ((line_count % 4) == 0) {
int salary = atoi(line);
salaries_array[(line_count / 4) - 1] = salary;
ret = 0;
}
}
fclose(fp);
}
return ret;
}
Now, create a function to get the average salary from the array:
int get_average_salary(int salaries_array[], int employees_count)
{
int salaries_sum = 0;
int i = 0;
for (; i < employees_count; i++) {
salaries_sum += salaries_array[i];
}
return salaries_sum / employees_count;
}
Putting it all together:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct
{
char name[10];
char nameEr[10];
int salaryannual;
char desig[10];
} Employee;
int get_number_of_employees(int *number_of_employees)
{
int ret = -1;
int line_count = 0;
FILE *fp = NULL;
if ((fp = fopen("employee.txt", "r")) != NULL) {
char line[64] = {0,};
while(fgets(line, sizeof(line), fp)) {
line_count++;
}
*number_of_employees = line_count / 4;
ret = 0;
fclose(fp);
}
return ret;
}
int get_employees_salaries(int salaries_array[], int employees_count)
{
int ret = -1;
int line_count = 0;
FILE *fp = NULL;
if ((fp = fopen("employee.txt", "r")) != NULL) {
char line[64] = {0,};
while(fgets(line, sizeof(line), fp)) {
line_count++;
if ((line_count % 4) == 0) {
int salary = atoi(line);
salaries_array[(line_count / 4) - 1] = salary;
ret = 0;
}
}
fclose(fp);
}
return ret;
}
int get_average_salary(int salaries_array[], int employees_count)
{
int salaries_sum = 0;
int i = 0;
for (; i < employees_count; i++) {
salaries_sum += salaries_array[i];
}
return salaries_sum / employees_count;
}
void main (int argc, char *argv[])
{
int employees_count = 0;
if (get_number_of_employees(&employees_count) == 0) {
int salaries_array[employees_count];
if (get_employees_salaries(salaries_array, employees_count) == 0) {
int average_salary = get_average_salary(salaries_array, employees_count);
printf("Average salary = %d\n", average_salary);
}
}
}

In order to dynamically allocate, you first need to decide on the allocation scheme. Here are a couple of schemes:
increment by a fixed number every time
increment by a fixed percentage
Unlike #jardellucca's method, even though the file is not read twice, these schemes require a lot of reallocation.
/* Increment by fixed number */
int increment = 5;
int employee_count = 0;
int employee_max = increment;
Employee* employees = malloc(sizeof(Employee) * employee_max);
...
void Save(Employee* emp)
{
/* check if there is enough space */
if (employee_count == employee_max)
{
employee_max += increment;
employees = realloc(employees, sizeof(Employee) * employee_max)
}
employees[employee_count] = *emp;
++employee_count;
}
If you have 1000 records, this method reallocs 200 times. An alternative is to increase by a percentage
int increment_pc = 150; /* 50% */
int employee_count = 0;
int employee_max = 10;
Employee* employees = malloc(sizeof(Employee) * employee_max);
...
void Save(Employee* emp)
{
/* check if there is enough space */
if (employee_count == employee_max)
{
employee_max = (employee_max * increment_pc) / 100;
employees = realloc(employees, sizeof(Employee) * employee_max)
}
employees[employee_count] = *emp;
++employee_count;
}
Using this allocation scheme, 1000 employees takes 13 reallocations but there is some wastage. The last allocation will allocate 1234, which is 234 over. In the previous scheme, it will, at most be 4 over. This is just a space/time problem.
You can waste time reading the file twice. You could, for instance be reading the file across a slow network.
You can waste time reallocating lots of times and have a few left over
You can waste space reallocating fewer times. You may be on a system that is tight on memory so a scheme like this may not be feasible.
The scheme you choose will be dependant upon the environment and the constraints. As for average salary, you can do sum up on the fly as the records are being saved and then just compute the average at the end.

Related

Can anyone help? I am trying to read data from a file but its just spitting out garbage

I am trying to read from file hw4.data and see if it has a name. The user inputs the name via a command line argument. Everything works fine but I can't get the file to be passed between the functions correctly. The assignment requires that I define the file in main and pass it between SCAN and LOAD.
#include <stdio.h>
#include <stdlib.h>
struct _data {
char name[20];
long number;
};
int SCAN(FILE *(*stream)) { // skim through the file and find how many entries there are
int size = 0;
char s_temp[100];
long l_temp;
while (1) {
fscanf(*stream, "%s %ld", s_temp, &l_temp);
if (feof(*stream)) break;
size++;
}
return size;
}
struct _data* LOAD(FILE *stream, int size) { // loop through the file and load the entries into the main data array
struct _data* d = malloc(size * sizeof(struct _data));
int i;
for (i = 0; i < size; i++) {
fscanf(stream, "%s %ld", d[i].name, &d[i].number);
}
return d;
}
void SEARCH(struct _data *BlackBox, char* name, int size) { // loop through the array and search for the right name
int i;
int found = 0;
for (i = 0; i < size; i++) {
printf("%s %s\n", BlackBox[i].name, name);
if (strcmp(BlackBox[i].name, name) == 0) {
printf("*******************************************\nThe name was found at the %d entry.\n*******************************************\n", i);
found = 1;
break;
}
}
if (found == 0) {
printf("*******************************************\nThe name was NOT found.\n*******************************************\n");
}
}
void FREE(struct _data* BlackBox, int size) { // free up the dynamic array
free(BlackBox);
}
int main(int argv, char* argc[]) {
if (argv == 2) {
printf("The argument supplied is %s\n", argc[1]);
FILE* file = fopen("./hw4.data", "r");
int size = SCAN(&file);
struct _data* data = LOAD(&file, size);
SEARCH(data, argc[1], size);
fclose(file);
return 0;
} else {
printf("*******************************************\n* You must include a name to search for.*\n*******************************************\n");
return 0;
}
}
Here's the format of hw4.data
ron 7774013
jon 7774014
tom 7774015
won 7774016
A few issues:
In SCAN, remove the feof. Replace with: if (fscanf(*stream, "%s %ld", s_temp, &l_temp) != 2) break;
Note that after calling SCAN, you should do: rewind(file);. Otherwise, LOAD will only see [immediate] EOF.
And, as others have mentioned, just pass file to SCAN/LOAD and not &file.
Add a check for null return from fopen (e.g.) if (file == NULL) { perror("fopen"); exit(1); }
Stylistically:
If you have a comment describing a function, put it on the line above the function.
Try to keep lines within 80 chars
Here is the refactored code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct _data {
char name[20];
long number;
};
// skim through the file and find how many entries there are
int
SCAN(FILE *stream)
{
int size = 0;
char s_temp[100];
long l_temp;
while (1) {
if (fscanf(stream, "%s %ld", s_temp, &l_temp) != 2)
break;
size++;
}
return size;
}
// loop through the file and load the entries into the main data array
struct _data *
LOAD(FILE *stream, int size)
{
struct _data *d = malloc(size * sizeof(struct _data));
int i;
for (i = 0; i < size; i++) {
fscanf(stream, "%s %ld", d[i].name, &d[i].number);
}
return d;
}
// loop through the array and search for the right name
void
SEARCH(struct _data *BlackBox, char *name, int size)
{
int i;
int found = 0;
for (i = 0; i < size; i++) {
printf("%s %s\n", BlackBox[i].name, name);
if (strcmp(BlackBox[i].name, name) == 0) {
printf("*******************************************\n");
printf("The name was found at the %d entry.\n", i);
printf("*******************************************\n");
found = 1;
break;
}
}
if (found == 0)
printf("*******************************************\n"
"The name was NOT found.\n"
"*******************************************\n");
}
// free up the dynamic array
void
FREE(struct _data *BlackBox, int size)
{
free(BlackBox);
}
int
main(int argv, char *argc[])
{
if (argv == 2) {
printf("The argument supplied is %s\n", argc[1]);
FILE *file = fopen("./hw4.data", "r");
if (file == NULL) {
perror("fopen");
exit(1);
}
int size = SCAN(file);
rewind(file);
struct _data *data = LOAD(file, size);
SEARCH(data, argc[1], size);
fclose(file);
}
else
printf("*******************************************\n"
"* You must include a name to search for.*\n"
"*******************************************\n");
return 0;
}
Using realloc, we can combine SCAN and LOAD into a single function:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct _data {
char name[20];
long number;
};
// loop through the file and load the entries into the main data array
struct _data *
LOAD(FILE *stream, int *sizep)
{
struct _data *all = NULL;
struct _data *d;
int size = 0;
int capacity = 0;
while (1) {
if (size >= capacity) {
capacity += 10;
all = realloc(all,sizeof(*all) * capacity);
if (all == NULL) {
perror("realloc");
exit(1);
}
}
d = &all[size++];
if (fscanf(stream, "%s %ld", d->name, &d->number) != 2)
break;
}
// trim to size actually used
all = realloc(all,sizeof(*all) * size);
*sizep = size;
return all;
}
// loop through the array and search for the right name
void
SEARCH(struct _data *BlackBox, char *name, int size)
{
int i;
int found = 0;
for (i = 0; i < size; i++) {
printf("%s %s\n", BlackBox[i].name, name);
if (strcmp(BlackBox[i].name, name) == 0) {
printf("*******************************************\n");
printf("The name was found at the %d entry.\n", i);
printf("*******************************************\n");
found = 1;
break;
}
}
if (found == 0)
printf("*******************************************\n"
"The name was NOT found.\n"
"*******************************************\n");
}
// free up the dynamic array
void
FREE(struct _data *BlackBox, int size)
{
free(BlackBox);
}
int
main(int argv, char *argc[])
{
if (argv == 2) {
printf("The argument supplied is %s\n", argc[1]);
FILE *file = fopen("./hw4.data", "r");
if (file == NULL) {
perror("fopen");
exit(1);
}
int size;
struct _data *data = LOAD(file, &size);
SEARCH(data, argc[1], size);
fclose(file);
}
else
printf("*******************************************\n"
"* You must include a name to search for.*\n"
"*******************************************\n");
return 0;
}
I had to use rewind() in order to reset the file so that LOAD() would read from the start of the file and give good data.

C problem with saving structure to text file and downloading it back to stdout

Hey I have a problem with my code project where I try to create a project that keeps up wit Olympic medals. I have a problem of creating a text file that contains the structure and is named by user. I also have a problem to download the structure.
PROBLEM: I have a problem to make a text file named by user that contains the structure and then download it back to stdout. I don't know how to fix my functions to do this correctly. Now my function save_file can't even produce the file.
Example if the input:
A Canada
A USA
M USA 2 1 1
M Canada 0 0 1
M USA 1 3 1
M USA -1 0 0
L
W medals
Q
I have defined my structure this way:
typedef struct Olympia
{
char* country;
int gold;
int silver;
int bronze;
}Olympia;
Then I have a function that adds country
int add_country(struct Olympia* data, char* str, int i)
{
if (str[0] == '\0') //checking that input is correct
{
printf("Error! Try again!\n");
}
else
{
data[i].country = malloc(strlen(str) + 2); //allocating memory for country name
strcpy(data[i].country, str); //adding country to database
data[i].gold = 0; //setting medals to zero
data[i].silver = 0;
data[i].bronze = 0;
i++;
}
return i;
}
Next I add medals to the each country
int update_medals(struct Olympia* data, char* str, int add_gold, int add_silver, int add_bronze, int i)
{
int a = 0;
int b = 0;
if (str[0] == '\0') //checking that input is correct
{
printf("Error! Try again!");
}
else
{
while (a < i)
{
if (strcmp(data[a].country, str) == 0) //adding medals to right country
{
data[a].gold = data[a].gold + add_gold;
data[a].silver = data[a].silver + add_silver;
data[a].bronze = data[a].bronze + add_bronze;
b++;
}
a++;
}
if (b == 0) //and if the country didn't participate to the olympics
{
printf("This country isn't in the Olympics! Try Again!\n");
}
}
}
Next there is print function
int print_data(struct Olympia* data, int i)
{
for (int a = 0; a < i; a++)
{
printf("%s %d %d %d\n", data[a].country, data[a].gold, data[a].silver, data[a].bronze);
}
}
And then there are the two function that doesn't work. What should I do?
Olympia *save_file(Olympia* data, const char* filename, int i)
{
if (strlen(filename) > 100)
{
printf("Filename is too long: Maxium lenght for filename is 100 characters");
return data;
}
char name[100];
int ret = sscanf(filename, "W %s", name);
if (ret != 1)
{
printf("Error! Try again!");
return data;
}
FILE* file = fopen(name, "w");
if (!file)
{
printf("Error saving file! Try again");
return data;
}
int a = 0;
while (data[a].country[0] != 0)
{
fprintf(file, "%s %d %d %d\n", data[a].country, data[a].gold, data[a].silver, data[a].bronze);
a++;
}
fclose(file);
return 0;
}
int load_file(struct Olympia* data, char* filename, int i)
{
int a = 0;
FILE* file = fopen(filename, "r");
if (!file)
{
printf("Error opening file! Try again");
}
struct Olympia* arr = malloc(sizeof(Olympia));
while (fscanf(file, "%s %d %d %d", data[a].country, data[a].gold, data[a].silver, data[a].bronze))
{
i++;
a++;
arr = realloc(arr, sizeof(Olympia) * (i + 2));
}
arr[a].country[0] = 0;
fclose(file);
return arr;
}
And the main function
int main(void)
{
char command;
int gold = 0;
int silver = 0;
int bronze = 0;
int i = 0;
char* line = (char*)malloc((100) * sizeof(char)); //allocating memory for one stdin line
char* countryname = (char*)malloc(20 * sizeof(char)); // allocating memory for country name
char* filename = (char*)malloc(100 * sizeof(char));
struct Olympia* countrydata = malloc(sizeof(struct Olympia) * 1); //allocating memory for structure
line = fgets(line, 100, stdin);
while(1)
{
sscanf(line, "%c %s %d %d %d", &command, countryname, &gold, &silver, &bronze);
switch (command)
{
case 'A':
i = add_country(countrydata, countryname, i);
countrydata = realloc(countrydata, sizeof(struct Olympia) * (i + 1));
break;
case 'M':
update_medals(countrydata, countryname, gold, silver, bronze, i);
break;
case 'L':
print_data(countrydata, i);
break;
case 'W':
save_file(countrydata, filename, i);
break;
case 'O':
i = load_file(countrydata,filename, i);
break;
case 'Q':
free(line);
free(countryname);
free(countrydata);
return(EXIT_SUCCESS);
}
line = fgets(line, 100, stdin);
if (line == NULL)
{
free(line);
free(countryname);
free(countrydata);
return(EXIT_SUCCESS);
}
}
}
You call save_file(countrydata, filename, i); without having set filename. Change to save_file(countrydata, line, i); since for whatever reason you expect the command character W to precede the name.
Then in save_file() the condition in while (data[a].country[0] != 0) is unusable, since the data element after the last one is not initialized. Use while (a < i) instead.

How can i automatically increase memory allocation for a struct in C?

I am building a small program that takes name and age as input (stored in a struct) and spits out the output. One of the problems that I am facing is I have to enter the number of people I am going to store, something that I am sure I can solve with realloc() it's just not working. Here is what I got so far.
#include <stdio.h>
#include<stdlib.h>
struct info
{
int age;
char name[30];
};
int main()
{
struct info *Ptr;
int i, num;
printf("Enter number of people");
scanf("%d", &num);
// Allocates the memory for num structures with pointer Ptr pointing to the base address.
Ptr = (struct info*)malloc(num * sizeof(struct info));
for(i = 0; i < num; ++i)
{
printf("Enter name and age:\n");
scanf("%s %d", &(Ptr+i)->name, &(Ptr+i)->age);
}
for(i = 0; i < num ; ++i)
printf("Name = %s, Age = %d\n", (Ptr+i)->name, (Ptr+i)->age);
return 0;
}
I have tried to realloc inside the first for loop, but it wasn't working even if it makes sense to have it there. Have also tried to convert the loop to a while loop like this:
while(input != "stop)
{
allocate more memory
}
How can I use realloc to in order to skip having to enter the persons number before entering them?
realloc is the correct way. Just start with Ptr = NULL and num = 0 and on each input increase the number of elements by one.
Remember to limit the number of characters scanf can read, otherwise you may buffer overrun.
Also I find Ptr[i] way easier then (Ptr+i)->.
Also compare strings with strcmp not using !=. The != will compare pointers to strings, not strings themselves.
As I like reading the whole line, then scanning the line, I would do it like this:
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
struct info
{
int age;
char name[30];
};
int main()
{
struct info *ptr = 0;
size_t num = 0;
for (;;) {
printf("Enter name and age. If you want to stop, type only 'stop'.\n");
char line[256];
if (fgets(line, sizeof(line), stdin) == NULL) {
fprintf(stderr, "fgets error");
exit(-1);
}
if (!strcmp("stop\n", line)) {
break;
}
struct info tmp;
if (sscanf(line, "%29s %d\n", tmp.name, &tmp.age) != 2) {
fprintf(stderr, "error parsing line\n");
exit(-1);
}
ptr = realloc(ptr, (num + 1) * sizeof(*ptr));
if (ptr == NULL) {
fprintf(stderr, "error allocating memory!\n");
exit(-1);
}
ptr[num] = tmp;
++num;
}
for (size_t i = 0; i < num ; ++i) {
printf("Name = %s, Age = %d\n", ptr[i].name, ptr[i].age);
}
free(ptr);
return 0;
}
If you are not sure of the no.of.elements you want to allocate and do it based on the users choice, then you can follow the below approach.
It starts with one element and the memory is reallocated as when the user wants to add new element.
#include <stdio.h>
#include<stdlib.h>
struct info
{
int age;
char name[30];
};
int main()
{
struct info *Ptr=NULL;
int i=0, num;
char c='Y';
while(c=='Y'||c=='y') {
Ptr=realloc(Ptr,(i+1)*sizeof(struct info));
if(Ptr==NULL)
break;
printf("Enter name and age:\n");
scanf("%s %d",&Ptr[i].name,&Ptr[i].age);
printf("Do you want to cont?\n");
scanf(" %c",&c);
i++;
}
num=i;
for(i = 0; i < num ; ++i)
printf("Name = %s, Age = %d\n", (Ptr+i)->name, (Ptr+i)->age);
free(Ptr);
return 0;
}
To answer exactly, you can first read the input to temp variables and check if you need to stop: Break the loop if so. Or continue and reallocate the 'storage' array by increasing it's size by one and copying the values you just read to the 'storage' array.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct info
{
int age;
char name[30];
};
int main()
{
struct info * infos = 0;
int num = 0;
char input_name[30];
int input_age;
while (1)
{
printf("Enter name and age:\n");
int r = scanf("%29s", input_name);
if (r == EOF || strcmp(input_name, "stop") == 0)
break;
scanf(" %d", &input_age);
infos = realloc(infos, sizeof(struct info) * (num + 1));
infos[num].age = input_age;
memcpy(infos[num].name, input_name, sizeof(char) * 30);
num++;
}
for(int i = 0; i < num ; ++i)
printf("Name = %s, Age = %d\n", infos[i].name, infos[i].age);
return 0;
}
You should use a data struct like vector.
vector_init init vector.
vector_push push val to vector, if necessary, will realloc memory.
vector_output output the vector.
The following code could work:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define INIT_SIZE 1
struct info
{
int age;
char name[30];
};
struct vector {
struct info* p;
int n;
int index;
};
void vector_init(struct vector* ve) {
ve->n = INIT_SIZE;
ve->index = 0;
ve->p = malloc(sizeof(struct info) * ve->n);
}
void vector_push(struct vector* ve, struct info* tmp) {
if (ve->n == ve->index) {
ve->n *= 2;
ve->p = realloc(ve->p, sizeof(struct info) * ve->n);
}
ve->p[ve->index++] = *tmp;
}
void vector_output(const struct vector* ve) {
for (int i = 0; i < ve->index; ++i)
printf("Name = %s, Age = %d\n", ve->p[i].name, ve->p[i].age);
}
int main()
{
struct vector ve;
vector_init(&ve);
for (;;) {
struct info tmp;
printf("Enter name and age:\n");
scanf("%29s", tmp.name);
if (strcmp(tmp.name, "stop") == 0)
break;
scanf("%d", &tmp.age);
vector_push(&ve, &tmp);
}
vector_output(&ve);
return 0;
}

C doesn't load info from file (fread, fwrite)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SLENG 50 //just a random value
typedef struct Song
{
char *name;
char *nameSong;
char *timeSong;
int date;
} Song;
void saveToFile(Song *x, int *songCount) //Saves info to the binary file
{
FILE *f = fopen("array.txt", "w");
if (f == NULL)
{
printf("Error\n");
}
fwrite(songCount, sizeof(int), 1, f);
fwrite(x, sizeof(struct Song), (*songCount), f);
fclose(f);
}
void readSong(Song *x, int *songCount) //Reads info fromt he file and writes it
{
FILE *fr = fopen("array.txt", "r");
if (fr == NULL)
{
printf("Error\n");
}
printf("Songs:\n");
fread(songCount, sizeof(int), 1, fr);
fread(x, sizeof(struct Song), (*songCount), fr);
for(int i=0; i < (*songCount); i++)
{
printf("%d. %s %s %s %d\n", (i+1), x[i].name, x[i].nameSong, x[i].timeSong, x[i].date);
}
fclose(fr);
}
void insertSong(Song *x, int Count) //Inserts new song into the array.
{
printf("\nInsert name of the band:\n");
x[Count].name=malloc(SLENG * sizeof(char));
scanf("%s", x[Count].name);
printf("Insert name of the song:\n");
x[Count].nameSong=malloc(SLENG * sizeof(char));
scanf("%s", x[Count].nameSong);
printf("Insert length of the song:\n");
x[Count].timeSong=malloc(SLENG * sizeof(char));
scanf("%s", x[Count].timeSong);
printf("Insert then song was created:\n");
scanf("%d", &(x[Count].date));
printf("\n");
}
main()
{
int songCount, menuOption;
Song *x=malloc(SLENG*sizeof(char)+SLENG*sizeof(char)+SLENG*sizeof(char)+sizeof(int));
printf("1. insert song\n 2. load from file\n ");
scanf("%d", &menuOption);
switch(menuOption)
{
case(1) :
printf("Insert how many songs do you want to input?\n");
scanf("%d", &songCount);
for(int i=0; i<songCount; i++)
{
insertSong(x, i);
}
saveToFile(x, &songCount);
break;
case(2) :
readSong(x, &songCount);
break;
}
}
I have an assingment to write a programm which would input some data into file and could read that data from that file, the problem is probably with fwrite or fread, couse it seems to crash everytime I try to load the and write the data from file. Any ideas why is it not working properly? And can I even do it like this as it is dynamic struct array. Thanks in advance.
In order to save the structure to a file, it must only contain scalar values, not pointers into memory objects. Modify your structure to use arrays instead of pointers:
typedef struct Song {
char name[SLENG];
char nameSong[SLENG];
char timeSong[SLENG];
int date;
} Song;
And modify the code accordingly, but note that:
saving and reading the structures to and from a file requires opening it in binary mode "wb" and "rb".
it is very misleading to name a binary file array.txt.
you do not need to pass the address of the count when writing to the file, but you need to pass the address of the array pointer when reading as you do not know yet how much memory to allocate.
Here is the modified code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SLENG 50 // this value is used in the file format
typedef struct Song {
char name[SLENG];
char nameSong[SLENG];
char timeSong[SLENG];
int date;
} Song;
int saveToFile(Song *x, int songCount) { //Saves info to the binary file
FILE *f = fopen("array.bin", "wb");
if (f == NULL) {
printf("Error\n");
return -1;
}
fwrite(songCount, sizeof(int), 1, f);
int written = fwrite(x, sizeof(struct Song), songCount, f);
fclose(f);
return written;
}
int readSong(Song **x, int *songCount) { //Reads info from the file and writes it
int count = 0;
FILE *fr = fopen("array.bin", "rb");
if (fr == NULL) {
printf("Error\n");
return -1;
}
printf("Songs:\n");
fread(&count, sizeof(int), 1, fr);
*x = calloc(count, sizeof(Song));
if (*x == NULL) {
printf("Cannot allocate %d bytes of memory\n", count);
fclose(fr);
return -1;
}
int found = fread(*x, sizeof(struct Song), count, fr);
for (int i = 0; i < found; i++) {
printf("%d. %s %s %s %d\n", i + 1,
(*x)[i].name, (*x)[i].nameSong, (*x)[i].timeSong, (*x)[i].date);
}
fclose(fr);
return *songCount = found;
}
void insertSong(Song *x, int Count) { //Inserts new song into the array.
printf("\nInsert name of the band:\n");
scanf("%49s", x[Count].name);
printf("Insert name of the song:\n");
scanf("%49s", x[Count].nameSong);
printf("Insert length of the song:\n");
scanf("%49s", x[Count].timeSong);
printf("Insert then song was created:\n");
scanf("%d", &(x[Count].date));
printf("\n");
}
int main(void) {
int songCount, menuOption;
Song *x = NULL;
printf("1. insert song\n 2. load from file\n ");
scanf("%d", &menuOption);
switch (menuOption) {
case 1:
printf("Insert how many songs do you want to input?\n");
if (scanf("%d", &songCount) == 1) {
x = calloc(songCount, sizeof(Song));
for (int i = 0; i < songCount; i++) {
insertSong(x, i);
}
saveToFile(x, songCount);
}
break;
case 2:
readSong(&x, &songCount);
break;
}
free(x);
x = NULL;
return 0;
}

C - Opening differents files using same pointer

I'm trying to retrieve informations by many plain-text files, which will be then stored in a proper struct. To do so, I'm using a function that takes member of the struct to populate and source of the plain-text file where the informations are stored.
Posting my "test" code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct _elem
{
const char *title;
int ok;
int almost;
int nope;
int hits;
float last_rank;
};
typedef struct _elem Chapter;
Chapter *generate_array(const char *source, int *elems);
int engine_start(Chapter *elem, char *source);
int main()
{
const char path_f[100];
int elements = 0;
int i = 0;
Chapter *dict;
printf("Insert the name of the source:\n");
scanf("%s", path_f);
printf("\nGenerating dictionary, please wait...\n");
dict = generate_array(path_f, &elements);
if (dict == NULL)
{
printf("Aborting.\n");
exit(1);
}
while (i < elements)
{
printf("Element %d:\n", (i + 1));
printf("\nTitle: %s\n", dict[i].title);
printf("Ok: %10d\n", dict[i].ok);
printf("Almost: %5d\n", dict[i].almost);
printf("Nope: %8d\n", dict[i].nope);
printf("Hits: %8d\n", dict[i].hits);
printf("Rank: %8.2f\n", dict[i].last_rank);
printf("\n");
i++;
}
return EXIT_SUCCESS;
}
Chapter *generate_array(const char *source, int *elems)
{
FILE *src;
int sources;
int i = 0;
char **srcs;
Chapter *generated;
src = fopen(source, "r");
if (src == NULL)
{
printf("[!!] Error while reading file!\n");
return NULL;
}
fscanf(src, "%d", &sources);
if (sources <= 0)
{
printf("[!!] Wrong number of sources, exiting.\n");
return NULL;
}
srcs = (char **) malloc(sizeof(char *) * sources);
while (i < sources && !feof(src))
{
srcs[i] = (char *) malloc(sizeof(char) * 100);
fscanf(src, "%s", srcs[i++]);
}
fclose(src);
generated = (Chapter *) malloc(sizeof(Chapter) * i);
*elems = i;
i = 0;
while (i < *elems)
{
if(engine_start( &generated[i], srcs[i] )) i++;
else
{
printf("[!!] Error in file %s, aborting.\n", srcs[i]);
return NULL;
}
}
return generated;
}
int engine_start(Chapter *elem, char *source)
{
FILE *parser;
int done = 0;
parser = fopen(source, "r");
if (parser == NULL) printf("[!!] Error while opening %s, aborting.\n", source);
else
{
fgets(elem->title, 100, parser);
fscanf(parser, "%d %d %d %d %f", &(elem->ok), &(elem->almost),
&(elem->nope), &(elem->hits),
&(elem->last_rank) );
fclose(parser);
done = 1;
}
return done;
}
Now this is the main file where are stored paths to the other plain-text files:
lol.dat
5
lold/lol1.dat
lold/lol2.dat
lold/lol3.dat
lold/lol4.dat
lold/lol5.dat
And one example of lolX.dat:
Qual'รจ la vittoria di cristo?
3 4 5 12 44.9
I'm getting SIGSEGV after the first iteration of "engine_start", probably due to FILE *parser (but I can be totally wrong, I don't know at this point).
Someone can guide me through this problem? Thank you.
Make the following changes and try-
struct _elem
{
char *title; // allocate the memory for this.
int ok;
int almost;
int nope;
int hits;
float last_rank;
};
You need to allocate memory for element title before assigning something to it.
int engine_start(Chapter *elem, char *source)
{
FILE *parser;
int done = 0;
parser = fopen(source, "r");
if (parser == NULL) printf("[!!] Error while opening %s, aborting.\n", source);
else
{
elem->title=(char *)malloc(100); // include this line.
fgets(elem->title, 100, parser);
fscanf(parser, "%d %d %d %d %f", &(elem->ok), &(elem->almost),
&(elem->nope), &(elem->hits),
&(elem->last_rank) );
fclose(parser);
done = 1;
}
return done;
}

Resources