Reading lines from file - c

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

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.

Why is fscanf() seg faulting while trying to read from a file?

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

Saving a string from a text file to a struct using fscanf (C)

Sample Text file:
234765 PETER
867574 SMITH
I'm trying to take the id and string from the text file and save it into a struct. The id is saving fine but the string isn't.
typedef struct student
{
int id[DATA_SIZE];
char *student[DATA_SIZE];
}studentinfo;
studentinfo list;
struct student *create_space(int size)
{
struct student *tmp = (struct student*)malloc(size*sizeof(struct student));
return(tmp);
}
struct student * readData(struct student*pointer,studentinfo v)
{
int count =0;
int tmpid;
char str[256];
FILE* in_file;
in_file = fopen("studentlist.txt","r");
while(fscanf(in_file,"%d",&tmpid)!= EOF && count<DATA_SIZE)
{
fscanf(in_file,"%s",v.student[count]);
//printf("%s\n",str );
v.id[count]=tmpid;
count++;
}
pointer =&v;
return pointer;
}
int main()
{
struct student *data;
struct student *sdata;
data = create_space(1);
sdata = readData(data,list);
//printf("%s\n",sdata->student[2] );
}
Their are a couple of issues:
fscanf() reads formatted input, and returns the number of items read.
This line:
while(fscanf(in_file,"%d",&tmpid)!= EOF && count<DATA_SIZE)
Could be this:
while (count < DATA_SIZE && fscanf(in_file, "%d %255s", &list.id[count], str) == 2) {
Which verifies that 2 values are being read on each line successfully.
You are not checking if in_file returns NULL. It's safe to do this. This goes the same for malloc().
You need to correctly create space for char *students[DATA_SIZE], as this is an array of char * pointers. Once you allocate space for this via malloc() or strdup(), then you can copy the contents into students.
Here is an example of doing such a thing:
while (count < DATA_SIZE && fscanf(in_file, "%d %255s", &list.id[count], str) == 2) {
/* allocate space for one student */
list.student[count] = malloc(strlen(str)+1);
if (!list.student[count]) {
printf("Cannot allocate string\n");
exit(EXIT_FAILURE);
}
/* copy it into array */
strcpy(list.student[count], str);
count++;
}
Here is an example that you can use to help achieve your desired result:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define DATA_SIZE 256
typedef struct {
int id[DATA_SIZE];
char *student[DATA_SIZE];
} studentinfo_t;
int main(void) {
FILE *in_file;
studentinfo_t list;
char str[DATA_SIZE];
size_t count = 0;
in_file = fopen("studentlist.txt", "r");
if (!in_file) {
fprintf(stderr, "%s\n", "Error reading file");
exit(EXIT_FAILURE);
}
while (count < DATA_SIZE && fscanf(in_file, "%d %255s", &list.id[count], str) == 2) {
list.student[count] = malloc(strlen(str)+1);
if (!list.student[count]) {
printf("Cannot allocate string\n");
exit(EXIT_FAILURE);
}
strcpy(list.student[count], str);
count++;
}
for (size_t i = 0; i < count; i++) {
printf("%d %s\n", list.id[i], list.student[i]);
}
return 0;
}

C Programming: Reading a file and storing in array of struct

I am trying to read a file test.txt via fscanf and store it in a array of struct. This is what I tried. Problem here is that fscanf is not working as it is supposed to. After reading the file, I am also trying to print it on screen, but it won't work.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Item {
double value;
int unit;
char name[50];
};
int load(struct Item* item, FILE* data);
void display(struct Item item, int variableA);
int main()
{
struct Item I;
int i;
char ck;
ck = fopen("test.txt", "r");
if (ck)
{
for (i = 0; i < 3; i++)
{
load(&I, ck);
display(I, 0); //DISPLAY FUNCTION THAT READS test.txt and DISPLAYS
}
fclose(ck);
}
return 0;
}
int load(struct Item* item, FILE* data)
{
fscanf(data, "%d,%.2lf,%s\n", &(*item).unit,&(*item).value,&(*item).name);
return 0;
}
void display(struct Item item, int variableA)
{
printf("|%3d |%12.2lf| %20s |***\n", item.unit, item.value, item.name);
return;
}
This is what I have in test.txt file:
205,11.20,John Snow
336,23.40,Winter is coming
220,34.20,You know nothing
Error: Program compiles with some warnings , but I get segmentation fault when I execute the code.
Any idea why?
Output Expectation: OUTPUT should be read from test.txt file and should be displayed on to the screen.
Multiple problems in the program:
1.
char ck;
ck = fopen("test.txt", "r");
fopen returns a FILE*, not a char, use
FILE* ck = fopen(...);
2.
fscanf(data, "%d,%.2lf,%s\n", &(*item).unit,&(*item).value,&(*item).name);
always check return value of fscanf, if it is smaller than the number of fields you requested, the following call to fscanf is unlikely to do what you expect. Also, *item.unit is the same as item->unit, use item->unit because it is shorter and cleaner:
int ret = fscanf(data, "%d,%lf,", &item->unit, &item->value);
if (ret != 3) { // error }
Third, %s matches a sequence of non-white-space characters, so when fscanf reads "John", it will stop, and the next fscanf call will get to read "Snow" while expecting an integer.
So to input a string with whitespace, use fgets instead, and remember to remove the newline character in the end.
Try following:
int main(void)
{
struct Item I;
int i;
FILE* ck;
int ret;
ck = fopen("test.txt", "r");
if (ck)
{
for (i = 0; i < 3; i++)
{
ret = load(&I, ck);
if (ret < 0)
break;
display(I, 0); //DISPLAY FUNCTION THAT READS test.txt and DISPLAYS
}
fclose(ck);
}
return 0;
}
int load(struct Item* item, FILE* data)
{
int ret = fscanf(data, "%d,%lf,", &item->unit, &item->value);
if (ret != 2) {
return -1;
}
fgets(item->name, sizeof item->name, data);
item->name[strlen(item->name)-1] = '\0';
return 0;
}
void display(struct Item item, int variableA)
{
printf("|%3d |%12.2lf| %20s |***\n", item.unit, item.value, item.name);
return;
}
It outputs:
$ ./a.out
|205 | 11.20| John Snow |***
|336 | 23.40| Winter is coming |***
|220 | 34.20| You know nothing |***
You can try this different approach.
It uses:
malloc,realloc to allocate and reallocate memory for array of structs. I assumed that much larger text files with more lines will be used and this allows the array to resize when needed to accommodate more information.
strtok to parse each peice of data between , delimeters, and then store them into the array of structures.
Checks return value of pointers to avoid segmentation faults.
Uses fgets to read each line of the file into a string, from which we can parse ourselves afterwards.
This is the proposed code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NAMESTRLEN 50
#define INITSIZE 3
#define MAXSIZE 100
typedef struct {
int unit;
double value;
char name[NAMESTRLEN+1];
} item_t;
typedef struct {
item_t *items;
int numlines;
} allitems_t;
allitems_t *initialize_arraystructs(void);
void print_arraystructs(allitems_t *allitems);
void read_insert_items(FILE *filestream, allitems_t *allitems);
void check_ptr(void *ptr, const char *msg);
int
main(void) {
allitems_t *allitems;
FILE *fp;
fp = fopen("test.txt", "r");
if (fp == NULL) {
fprintf(stderr, "%s\n", "Error reading file!\n");
exit(EXIT_FAILURE);
}
allitems = initialize_arraystructs();
read_insert_items(fp, allitems);
print_arraystructs(allitems);
return 0;
}
void
read_insert_items(FILE *filestream, allitems_t *allitems) {
int count = 0;
char line[MAXSIZE];
char *unit, *value, *name;
size_t numitems = INITSIZE;
allitems->items = malloc(numitems * sizeof(item_t));
check_ptr(allitems->items, "Initial Allocation");
while (fgets(line, MAXSIZE, filestream) != NULL) {
unit = strtok(line, ",");
value = strtok(NULL, ",");
name = strtok(NULL, "\n");
if (count == numitems) {
numitems *= 2;
allitems->items = realloc(allitems->items, numitems * sizeof(item_t));
check_ptr(allitems->items, "Reallocation");
}
allitems->items[count].unit = atoi(unit);
allitems->items[count].value = atof(value);
strcpy(allitems->items[count].name, name);
count++;
allitems->numlines++;
}
}
allitems_t
*initialize_arraystructs(void) {
allitems_t *allitems;
allitems = malloc(sizeof(allitems_t));
check_ptr(allitems, "Initial Allocation");
allitems->items = NULL;
allitems->numlines = 0;
return allitems;
}
void
print_arraystructs(allitems_t *allitems) {
int i;
for (i = 0; i < allitems->numlines; i++) {
printf("%d,%.2f,%s\n",
allitems->items[i].unit,
allitems->items[i].value,
allitems->items[i].name);
}
}
void
check_ptr(void *ptr, const char *msg) {
if (!ptr) {
printf("Unexpected null pointer: %s\n", msg);
exit(EXIT_FAILURE);
}
}

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