Your code has 2 main issues :
- In readCSV you have a loop to read all the lines in the CSV but all data you are reading is stored always in the same (non-array) variable. You need to pass a Vector, rather than a Person to readCSV and store the data using people->personArray[index].some_field .
- Each time fgets() is called it overwrites line[1024]. You need to make a copy of the parsed data before calling fgets() again. I've used strdup for that .
Printing worked in your code because you printed the parsed data before calling fgets(). But issue 2 stops you from being able to separate parsing and printing.
This code fixes those two issues and moves printing to a different function doing minimal editing. I've renamed person.c to vector.c since the functions there deal with the vector as a whole rather than a single person.
//person.h
#ifndef PERSON_H_
#define PERSON_H_
typedef struct
{
const char *firstName, *lastName;
double weight;
}Person;
#endif
//vector.h
#ifndef VECTOR_H_
#define VECTOR_H_
#include "person.h"
typedef struct
{
Person *personArray;
int sizeArray;
int count;
}Vector;
extern void readCSV(Vector* people);
extern void printCSV(Vector* people);
#endif
//vector.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include "vector.h"
void initialize(Vector *v)
{
v->sizeArray = 10;
v->count = 0;
v->personArray = (Person*) malloc(v->sizeArray*sizeof(Person));
}
void readCSV(Vector* people)
{
FILE * fp;
char line[1024];
int index = 0;
fp = fopen("mycsvfile.csv","r");
while(fgets(line,sizeof(line),fp))
{
people->personArray[index].firstName = strdup(strtok(line,","));
people->personArray[index].lastName = strdup(strtok(NULL,","));
people->personArray[index].weight = atof(strtok(NULL,","));
++index;
}
people->count = index;
fclose(fp);
}
void printCSV(Vector* people)
{
int i;
for( i=0; i<people->count; ++i )
{
printf("%s %s %f\n",
people->personArray[i].firstName,
people->personArray[i].lastName,
people->personArray[i].weight);
}
}
//main.c
#include<stdio.h>
#include<stdlib.h>
#include "vector.h"
int main()
{
Vector people;
initialize(&people);
readCSV(&people);
printCSV(&people);
return 0;
}
That code works and should get you to continue with your work or assignment. But in order to become quality code worthy of production status many issues remain to be solved :
- Freeing memory used. Memory allocaded with malloc, strdup, etc needs to be freed when no longer used. In this minor program it will be freed by the C runtime when main exits but when your code becomes part of a bigger project it will be an important issue. Better to get used to it now.
- Dealing with unconstrained input. Try to run the program using an input file with 11 entries, it may crash or exhibit undefined behavior. This is because we allocate 10 entries in initialize(Vector* v). To solve it you can use a growable data type, like linked lists. Or you can parse the file twice; once discarding data to determine how many entries it has, then allocate the array and then a second one to actually read and store the data.
- Dealing with incorrect input. You need to specify the format of your CSV file. Are empty fields allowed? Maximum length of line? And verify the correctness of the input file, delivering an error if it does not parse rather than reading incorrect input. What happens if the file does not exist or can't be read?
- Make your code generic. readCSV(Vector* people) should become readCSV(Vector*, const char* filename). And you can read the filename from the command line using main(int argc, char* argv[]) rather than using a constant string.
- Beware of strtok() and fgets() behavior! Lines read with fgets() (except maybe the last one) will end in '\n'. In a line ending with "83.5\n" the last token will be "83.5\n". Notice the carriage return there. That may not be what you want. In your code it works because atof stops parsing at '\n'. If you don't want a \n in your token you can use strtok( line_or_NULL, ",\n")
- Deal properly with your Vector status. What happens if you call readCSV() twice? What if initialize() is called twice()? Safest practice would be to detect it, since it is a fast check, and abort the program with an error code if incorrect calls are made, or return an error code.
- Document code. A header to each function explaining what it does, parameters and what it returns It is very important to list preconditions and postconditions, like allowing or not to call initialize twice on the same Vector.
- Unit tests.