#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;
}
Related
I am using C in Visual Studio via a remote Linux server. I want to read a file and store the contents in a array of structs. Every time I try to read a line using fscanf() it seg faults. Thanks in advance.
Format of the file I'm trying to read:
F150 5.4 28000 white
RAM1500 5.7 32000 orange
car 4.5 12000 green
truck 6.1 55000 black
Here's a simplified version my program as the other parts function fine:
#include <stdlib.h>
#include <stdio.h>
struct data {
char name[20];
float floatNum;
int intNum;
char color[20];
} temp;
int scan(void) {
int size = 0;
FILE *data;
data = fopen("./hw3.data", "r");
while (1) {
fscanf(data, "%s %f %d %s", temp.name, &temp.floatNum,
&temp.intNum, temp.color);
if (feof(data))
break;
size++;
}
return size;
}
void load(int size, struct data autos[]) {
int i;
FILE *data;
data = fopen("./hw3.data", "r");
for (i = 0; i < size; i++) {
fscanf(data, "%s %f %d %s", autos[i].name, &autos[i].floatNum,
&autos[i].intNum, autos[i].color);
}
}
int main() {
int size;
struct data *autos;
size = scan();
autos = malloc(size * sizeof(struct data));
load(size, autos);
return 0;
}
There are multiple possible causes for problems:
you do not test if fopen() succeeds: if the file cannot be opened, the FILE pointer data will be null, causing undefined behavior in fscanf(), possibly a seg fault.
you do not test if malloc succeeds... again causing a seg fault if memory cannot be allocated.
you should close the FILE after reading
your test for feof() is incorrect: it might be true after successfully reading the last item, causing it to be ignored and you might never reach the end of file if one of the items cannot be read. You should just test the return value of fscanf(): it returns the number of successful conversions, so 4 in your case.
you should use %19s to avoid writing beyond the end of the targets arrays, another potential source of undefined behavior.
Here is a modified version:
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct data {
char name[20];
float floatNum;
int intNum;
char color[20];
};
int scan(const char *filename) {
struct data temp;
int size = 0;
FILE *data = fopen(filename, "r");
if (data == NULL) {
fprintf(stderr, "cannot open %s: %s\n", filename, strerror(errno));
return -1;
}
while (fscanf(data, "%19s %f %d %19s",
temp.name, &temp.floatNum,
&temp.intNum, temp.color) == 4) {
size++;
}
fclose(data);
return size;
}
int load(const char *filename, int size, struct data autos[]) {
int i;
FILE *data = fopen(filename, "r");
if (data == NULL) {
fprintf(stderr, "cannot open %s: %s\n", filename, strerror(errno));
return -1;
}
for (i = 0; i < size; i++) {
if (fscanf(data, "%19s %f %d %19s",
autos[i].name, &autos[i].floatNum,
&autos[i].intNum, autos[i].color) != 4)
break;
}
fclose(data);
return i;
}
int main() {
char filename[] = "./hw3.data";
int size = scan(filename);
if (size < 0)
return 1;
if (size > 0) {
struct data *autos = malloc(size * sizeof(struct data));
if (autos == NULL) {
fprintf(stderr, "cannot allocate %zu bytes\n", size * sizeof(struct data));
return 1;
}
int n = load(filename, size, autos);
for (int i = 0; i < n; i++) {
printf("%s %g %d %s\n", autos[i].name, autos[i].floatNum,
autos[i].intNum, autos[i].color);
}
free(autos);
}
return 0;
}
I need to solve a problem. I need to create two functions which scan user's input information and put in into structure array and then put all array to BIN file (with function fwrite()) and other function - read from BIN file with function fread(). You can see my code below. Problem is that I can write to file, but when I read I get filler array element and other element which are empty. How to get only filled struct array element?
#include <stdio.h>
#include <stdlib.h>
#include <mem.h>
typedef struct
{
char Lesson[50];
char TeachersName[50];
char TeachersLastName[50];
int Credits;
int NumberOfStudents;
} School;
void ToFile (char *fileName);
void FromFile (char *FileName);
int main()
{
char *fileName[]={"student.bin"};
ToFile (*fileName);
FromFile (*fileName);
return 0;
}
void ToFile (char *fileName)
{
int n, chars;
FILE *fp;
fp = fopen(fileName, "wb");
School Info[20];
if(fp == NULL)
{
printf("Error opening file\n");
exit(1);
}
printf("Testing fwrite() function: \n\n");
printf("Enter the number of records you want to enter: ");
scanf("%d", &n);
for(int i = 0;i<n;i++)
{
printf("\nEnter details of employee %d \n", i + 1);
fflush(stdin);
printf("Lesson: ");
gets(Info[i].Lesson);
printf("Teachers name: ");
gets(Info[i].TeachersName);
printf("Teachers last name: ");
gets(Info[i].TeachersLastName);
printf("Credits: ");
scanf("%d", &Info[i].Credits);
printf("Number of studens: ");
scanf("%d", &Info[i].NumberOfStudents);
chars = fwrite(&Info[i], sizeof(Info), 1, fp);
printf("Number of items written to the file: %d\n", chars);
}
fclose(fp);
}
void FromFile (char *FileName)
{
School Info[20]= { { "", "","",0,0 } };;
FILE * fpp;
fpp = fopen(FileName, "rb");
fread(&Info, sizeof(Info), 1, fpp);
/*
for(int j=0; j<20; ++j) {
printf("\nLesson: %s", Info[j].Lesson);
printf("\nTeachers name: %s", Info[j].TeachersName);
printf("\nTeachers last name: %s", Info[j].TeachersLastName);
printf("\nCredits: %d", Info[j].Credits);
printf("\nNumber of students: %d", Info[j].NumberOfStudents);
printf("\n");
}*/
int j=0;
while (fread(&Info, sizeof(Info), 1, fpp))
{
printf("\nLesson: %s", Info[j].Lesson);
printf("\nTeachers name: %s", Info[j].TeachersName);
printf("\nTeachers last name: %s", Info[j].TeachersLastName);
printf("\nCredits: %d", Info[j].Credits);
printf("\nNumber of students: %d", Info[j].NumberOfStudents);
printf("\n");
j++;
}
fclose(fpp);
}
I am coding a song database. For now, the data of 2 songs are stored in a text file, one struct field per line. I would like to copy the content of the file line by line into an array, and I do not get why the program crashes after calling load(). Is it a problem related to fgets()? Or when I replace '\n' by '\0'?
Here are the interesting parts of the code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include "functions.h"
int main()
{
int menu;
bool exit = false;
/*char title[256];
char artist[256];
char album[256];*/
int year;
Song **songs = NULL; // base pointer to the array (of pointers to struct)
FILE *f; // pointer to a file structure
int n = 0; // height of array at the beginning (= number of pointers to struct)
int i;
f = fopen("database.txt", "r+");
if(f == NULL)
return 0;
count_songs_db(f, &n); // will modify n (height of array) according to the number of existing songs
printf("n = %d\n", n);
songs = (Song**)malloc(n*sizeof(Song));
load(f, songs, n);
// MENU
for(i = 0 ; i < n ; ++i)
free(songs[i]);
free(songs);
fclose(f);
return 0;
}
functions:
void count_songs_db(FILE *f, int *n) // calculate how many songs there are already in the database.
{
int c, n_lines = 0;
while ((c = getc(f)) != EOF)
if (c == '\n')
++n_lines; // count number of lines
*n = n_lines/6; // 1 song = 6 lines. Changes the height of array accordingly.
rewind(f); // go back to beginning of file, to be able to load the db
}
void load(FILE *f, Song **songs, int n) // load existing songs (in the file) into the array
{
int i;
for(i = 0 ; i < n ; ++i)
{
fgets(songs[i]->title, 256, f); // reads a line of text
songs[i]->title[strlen(songs[i]->title)-1] = '\0'; // to replace \n by \0 at the end // not working?
fgets(songs[i]->artist, 256, f);
songs[i]->title[strlen(songs[i]->artist)-1] = '\0';
fgets(songs[i]->album, 256, f);
songs[i]->title[strlen(songs[i]->album)-1] = '\0';
fscanf(f, "%d\n", &(songs[i]->year)); // use it like scanf
fgets(songs[i]->genre, 256, f);
songs[i]->title[strlen(songs[i]->genre)-1] = '\0';
fscanf(f, "%d:%d\n", &(songs[i]->length.m), &(songs[i]->length.s));
}
for(i = 0 ; i < n ; ++i)
{
printf("Title: %s\n", songs[i]->title);
printf("Artist: %s\n", songs[i]->artist);
printf("Album: %s\n", songs[i]->album);
printf("Year of release: %d\n", songs[i]->year);
printf("Genre: %s\n", songs[i]->genre);
printf("Length: %d:%d\n", songs[i]->length.m, songs[i]->length.s);
}
}
struct:
typedef struct Length {
int m, s;
} Length;
typedef struct Song {
char title[256];
char artist[256];
char album[256];
int year;
char genre[256];
Length length;
} Song;
Thanks for your help.
Edit: I modified the code to use a simple array of struct. Here is the add_song() function and save() function:
void add_song(Song *songs, int *n)
{
printf("Title: ");
read(songs[*n].title, MAX_SIZE); // another function is used instead of scanf(), so the user can enter string with spaces. Also more secure.
printf("Artist: ");
read(songs[*n].artist, MAX_SIZE);
printf("Album: ");
read(songs[*n].album, MAX_SIZE);
printf("Year of release: ");
songs[*n].year = read_long(); // still have to check the user inputs (ie. year has to be between 1900 and 2017)
printf("Genre: ");
read(songs[*n].genre, MAX_SIZE);
printf("Length: \nmin: ");
songs[*n].length.m = read_long();
printf("sec: ");
songs[*n].length.s = read_long();
++(*n);
}
void save(FILE *f, Song *songs, int n) // save song in file
{
fprintf(f, "%s\n%s\n%s\n%d\n%s\n%d:%d\n", songs[n-1].title, songs[n-1].artist, songs[n-1].album, songs[n-1].year, songs[n-1].genre, songs[n-1].length.m, songs[n-1].length.s); // use it like printf. Prints the data in the file.
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
typedef struct Length {
int m, s;
} Length;
typedef struct Song {
char title[256];
char artist[256];
char album[256];
int year;
char genre[256];
Length length;
} Song;
void count_songs_db(FILE *f, int *n) // calculate how many songs there are already in the database.
{
int c, n_lines = 0;
while ((c = getc(f)) != EOF)
if (c == '\n')
++n_lines; // count number of lines
*n = n_lines / 6; // 1 song = 6 lines. Changes the height of array accordingly.
rewind(f); // go back to beginning of file, to be able to load the db
}
void load(FILE *f, Song *songs, int n) // load existing songs (in the file) into the array
{
int i;
for (i = 0; i < n; ++i)
{
fgets(songs[i].title, 256, f); // reads a line of text
songs[i].title[strlen(songs[i].title) - 1] = '\0'; // to replace \n by \0 at the end // not working?
fgets(songs[i].artist, 256, f);
songs[i].title[strlen(songs[i].artist) - 1] = '\0';
fgets(songs[i].album, 256, f);
songs[i].title[strlen(songs[i].album) - 1] = '\0';
fscanf(f, "%d\n", &(songs[i].year)); // use it like scanf
fgets(songs[i].genre, 256, f);
songs[i].title[strlen(songs[i].genre) - 1] = '\0';
fscanf(f, "%d:%d\n", &(songs[i].length.m), &(songs[i].length.s));
}
for (i = 0; i < n; ++i)
{
printf("Title: %s\n", songs[i].title);
printf("Artist: %s\n", songs[i].artist);
printf("Album: %s\n", songs[i].album);
printf("Year of release: %d\n", songs[i].year);
printf("Genre: %s\n", songs[i].genre);
printf("Length: %d:%d\n", songs[i].length.m, songs[i].length.s);
}
}
int main()
{
int menu;
bool exit = false;
/*char title[256];
char artist[256];
char album[256];*/
int year;
Song *songs = NULL; // base pointer to the array (of pointers to struct)
FILE *f; // pointer to a file structure
int n = 0; // height of array at the beginning (= number of pointers to struct)
int i;
f = fopen("database.txt", "r+");
if (f == NULL)
return 0;
count_songs_db(f, &n); // will modify n (height of array) according to the number of existing songs
printf("n = %d\n", n);
songs = (Song*)malloc(n * sizeof(Song));
load(f, songs, n);
// MENU
free(songs);
fclose(f);
return 0;
}
I am trying to read strings and integers from a simple text file to my array. But the problem is that I get some random characters in a line in the middle of my list. It probably has to do with a newline problem, but I am not sure. The text file looks like this:
4
Mr Tambourine Man
Bob Dylan
1965
Dead Ringer for Love
Meat Loaf
1981
Euphoria
Loreen
2012
Love Me Now
John Legend
2016
The first number (4), indicates how many songs there are in the list. I have made a struct which will be able to hold the songs and dynamically allocate memory for each pointer.
Struct:
typedef struct Song {
char *song;
char *artist;
int *year;
} Song;
Allocated:
Song *arr;
arr = (Song*)malloc(sizeof(Song));
Function:
int loadFile(char fileName[], Song *arr, int nrOf) {
FILE *input = fopen(fileName, "r");
if (input == NULL) {
printf("Error, the file could not load!\n");
} else {
int i = 0;
fscanf(input, "%d\n", &nrOf);
for (int i = 0; i < nrOf; i++) {
arr[i].song = (char*)malloc(sizeof(char));
arr[i].artist = (char*)malloc(sizeof(char));
arr[i].year = (int*)malloc(sizeof(int));
fgets(arr[i].song, 100, input);
fgets(arr[i].artist, 100, input);
fscanf(input, "%d\n", arr[i].year);
}
printf("The file is now ready.\n");
fclose(input);
}
return nrOf;
}
Are you able to find the problem? Or do you have a better solution?
This is wrong:
arr[i].song = (char*)malloc(sizeof(char));
arr[i].artist = (char*)malloc(sizeof(char));
You are only allocating buffers of size 1, there's no scaling. This gives you undefined behavior when you overrun the buffers by loading more data into them than they can hold.
I would expect those to read:
arr[i].song = malloc(100);
and so on. Note that no cast is necessary, and sizeof (char) is always 1.
Also, this:
arr[i].year = (int*)malloc(sizeof(int));
is super-strange. There's absolutely no reason to dynamically allocate a single integer, just make the field an int and store the value there directly.
First Issue:
arr[i].song = (char*)malloc(sizeof(char));
arr[i].artist = (char*)malloc(sizeof(char));
Are only allocating 1 byte for your char* pointers, song and artist. You can allocate a size for this:
arr[i].song = (char*)malloc(100 * sizeof(char)); /* or malloc(100) */
arr[i].artist = (char*)malloc(100 * sizeof(char));
Or you can simply malloc() enough space from you buffer:
char buffer[100];
fgets(buffer, 100, input);
/* check for failure, remove newline */
arr[i].song = malloc(strlen(buffer)+1);
/* check error from malloc */
strcpy(arr[i].song, buffer);
Or even use strdup():
arr[i].song = strdup(buffer);
Which is a substitute for malloc()/strcpy().
Note: You can also read Do I cast the result of malloc?.
Second Issue:
Your current struct:
typedef struct Song {
char *song;
char *artist;
int *year;
} Song;
Can be simplified to:
typedef struct {
char *song;
char *artist;
int year;
} Song;
Because year does not need to be a pointer. Easier to manage if its just an int. This avoids having to do allocations like:
arr[i].year = (int*)malloc(sizeof(int));
Other Recommendations:
You should check the return of fscanf() and fgets() as its safe to do this. It helps just incase your file will have incorrect data. This goes the same for malloc(), which can return NULL is unsuccessfully allocated on the heap.
Here is some code with the above considerations in mind:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SIZE 100
typedef struct {
char *song;
char *artist;
int year;
} Song;
Song *create_array(FILE *input, int *nrof);
void load_data(Song *arr, FILE *input, int nrof);
void print_free_data(Song *arr, int nrof);
void get_buffer(char buffer[], FILE *input);
int main(void) {
FILE *input;
Song *arr;
int nrof;
input = fopen("artist.txt", "r");
if (input == NULL) {
fprintf(stderr, "Error reading file\n");
exit(EXIT_FAILURE);
}
arr = create_array(input, &nrof);
load_data(arr, input, nrof);
print_free_data(arr, nrof);
fclose(input);
return 0;
}
Song *create_array(FILE *input, int *nrof) {
Song *arr;
if (fscanf(input, "%d ", nrof) != 1) {
fprintf(stderr, "Cannot find number of songs\n");
exit(EXIT_FAILURE);
}
arr = malloc(*nrof * sizeof(*arr));
if (arr == NULL) {
fprintf(stderr, "Cannot allocate %d spaces for array\n", *nrof);
exit(EXIT_FAILURE);
}
return arr;
}
void load_data(Song *arr, FILE *input, int nrof) {
char buffer[SIZE];
for (int i = 0; i < nrof; i++) {
get_buffer(buffer, input);
arr[i].song = malloc(strlen(buffer)+1);
if (arr[i].song == NULL) {
fprintf(stderr, "Cannot allocate song\n");
exit(EXIT_FAILURE);
}
strcpy(arr[i].song, buffer);
get_buffer(buffer, input);
arr[i].artist = malloc(strlen(buffer)+1);
if (arr[i].artist == NULL) {
fprintf(stderr, "Cannot allocate artist\n");
exit(EXIT_FAILURE);
}
strcpy(arr[i].artist, buffer);
if (fscanf(input, "%d ", &arr[i].year) != 1) {
fprintf(stderr, "Cannot find year for Song: %s Album: %s\n",
arr[i].song, arr[i].artist);
exit(EXIT_FAILURE);
}
}
}
void get_buffer(char buffer[], FILE *input) {
size_t slen;
if (fgets(buffer, SIZE, input) == NULL) {
fprintf(stderr, "Error from fgets(), line not read\n");
exit(EXIT_FAILURE);
}
slen = strlen(buffer);
if (slen > 0 && buffer[slen-1] == '\n') {
buffer[slen-1] = '\0';
} else {
fprintf(stderr, "Too many characters entered\n");
exit(EXIT_FAILURE);
}
}
void print_free_data(Song *arr, int nrof) {
for (int i = 0; i < nrof; i++) {
printf("%s\n%s\n%d\n\n", arr[i].song, arr[i].artist, arr[i].year);
free(arr[i].song);
arr[i].song = NULL;
free(arr[i].artist);
arr[i].artist = NULL;
}
free(arr);
arr = NULL;
}
Which Outputs correct data:
Mr Tambourine Man
Bob Dylan
1965
Dead Ringer for Love
Meat Loaf
1981
Euphoria
Loreen
2012
Love Me Now
John Legend
2016
Your memory allocation is incorrect. The structure should have char arrays for the song and artist names and an int for the year, and you should modify your API to return the array and its size to the caller:
int loadFile(const char *fileName, Song **arr, int *numberp);
Here is a corrected and simplified of your program:
#include <stdio.h>
#include <stdlib.h>
typedef struct Song {
char song[100];
char artist[100];
int year;
} Song;
/* call as
if (loadFile(fileName, &songs, &songs_size) < 0) {
// deal with error...
}
*/
int loadFile(const char *fileName, Song **arrp, int *numberp) {
FILE *input;
Song *arr;
int i, nrOf;
input = fopen(fileName, "r");
if (input == NULL) {
fprintf(stderr, "Cannot open file %s\n", filename);
return -1;
} else {
if (fscanf(input, "%d\n", &nrOf) != 1) {
fprintf(stderr, "%s: missing number of items\n", filename);
fclose(intput);
return -1;
}
arr = calloc(sizeof(*arr), nrOf);
if (arr == NULL) {
fprintf(stderr, "cannot allocate memory for %d items\n", nrOf);
fclose(intput);
return -1;
}
for (int i = 0; i < nrOf; i++) {
char cc;
if (fscanf(input, "%99[^\n]%*c%99[^\n]%*c%d%c",
sarr[i].song, arr[i].artist,
&arr[i].year, &cc) != 4 || cc != '\n') {
fprintf(stderr, "%s: invalid format for item %d\n",
filename, i);
break;
}
}
printf("The file is now ready.\n");
fclose(input);
*arrp = arr;
*numberp = i;
return i;
}
}
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;
}