0

I have a csv file with information where the parts of it are separated with a ",". What I want to do is take this information and store it in different struct variables but I don't know how to make it so that the information is stored there.

This is my main file:

struct items beer [100];

int main(int argc, char **argv) {
    char *oneline, *tok;
    char envara[512];
    char delim [] = ",";
    FILE *fp;
    int i = 0;
    fp = fopen("varor.csv", "r");

    printf("ID\n");

    while (!feof(fp)) {
        if (fp == NULL) {
            fprintf(stderr, "File varor.csv could not be opened\n");
            exit(-1);
        }
        fgets(envara, 512, fp);
        envara[strlen(envara) -1] = '\0'; 

        printf("En rad; %s\n", envara);

        oneline = strdup(envara);
        tok = strtok(oneline, delim);

        while (tok != NULL) {
            printf("%s\n", tok);
            tok = strtok(NULL,delim);
        }
    }
    return 0;
}

And this is my struct:

struct items {
    int itemnumber;
    char name [100];
    float price;
    float volyme;
    char types [100];
    char style [100];
    char package [20];
    char country [20];
    char producer [50];
    float alcohol_amount;
};
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • Look at the 'Frequent' C tab, second entry:( – Martin James Dec 29 '17 at 14:29
  • When you fix that, you are going to need to write some actual code to load up the struct fields from your tokenized line. – Martin James Dec 29 '17 at 14:31
  • Could you link it ? –  Dec 29 '17 at 14:31
  • 2
    Baby steps - first just open the file, read all the lines and print them out. Don't do anything else at all until that 100% works - no missing lines, no extra line at start, no extra line at end. Then strtok each line and print out each token. Don't do anything else at all until that 100% works - no missing tokens/fieds, no copies, no extras, just 100% what you expect. Only then, think about loading the struct/s. – Martin James Dec 29 '17 at 14:34
  • Oh ff.. https://stackoverflow.com/questions/5431941/why-is-while-feof-file-always-wrong – Martin James Dec 29 '17 at 14:35
  • So it's wrong to use !feof ? I have noticed that it prints the last line of my text file 1 extra time rather than stopping at it. –  Dec 29 '17 at 14:48
  • 1
    *So it's wrong to use !feof ?* Not **always** but: *I have noticed that it prints the last line of my text file 1 extra time* That's one really common reason it's used wrong. – Andrew Henle Dec 29 '17 at 15:02
  • And this is not only not needed, it leaks: `oneline = strdup(envara);` If you call `strdup()`, you have to `free()` the string it returns. Given the code you've posted, there's no reason to call `strdup()` at all. – Andrew Henle Dec 29 '17 at 15:05

1 Answers1

3

Reading a file line-by-line should be done using fgets. It will return a null pointer if the stream you read is at end-of-file. Using feof is wrong.

Despite tokenizing the read line is sometimes a better approach, you can use the sscanf too to extract data from each line into your struct items. To make my example easier to read, I've changed the struct items as follows:

typedef struct item_t {
    char name[20];
    float weight;
    unsigned long id;
    float price;
} Item;

If you don't have a header line in your CSV file, you can parse it as simple as:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_ITEM 5

typedef struct item_t {
    char name[20];
    float weight;
    unsigned long id;
    float price;
} Item;

int main() {
    Item items[MAX_ITEM];
    int i = 0, parsedData;

    char line[512];
    FILE* csvFile = fopen("test.csv", "r");

    if(csvFile == NULL) {
        perror("Failed to open file.");
        exit(EXIT_FAILURE);
    }

    while(fgets(line, sizeof(line), csvFile) != NULL && i < MAX_ITEM) {
        parsedData = sscanf(line, "%20[^,],%f,%lu,%f",
                items[i].name, &items[i].weight, &items[i].id, &items[i].price);
        if(parsedData == 4)
            items[i++].name[19] = '\0';
        else
            fprintf(stderr, "Format error in line: %s\n", line);
    }

    fclose(csvFile);
    return 0;
}

In my example the first value can contain spaces too, the %20[^,] part of the format string means: read anything that is not a comma character up to 20 characters.

Live Demo

Note: using the sscanf function is not safe, it can cause overflows and undefined behaviours in many ways. So, it's your responsibility to keep the fields of your CSV file parsable by the format string, and to keep the parameter list of the sscanf function proper. My example will skip the current line if it contains more than 20 characters before the first comma, but e.g. cannot prevent integer overflows if you specify too long number for id. To write safer code, consider writing your own conversion functions for each different fields with appropriate input checks, then use e.g. the tokenization of lines with these functions to fill your struct items.

Akira
  • 4,385
  • 3
  • 24
  • 46
  • And what happens if the data has more than 19 characters before the first comma? `*scanf()` is nasty. – Andrew Henle Dec 29 '17 at 17:47
  • @AndrewHenle, `*scanf()` stops at the first mismatch, so in my example it won't cause buffer overflow on `char name[20]` but the rest of the data won't be parsed. Anyway, you are right, using `*scanf()` is not the safest way. I added it as a notice to my answer. – Akira Dec 30 '17 at 11:00