user1803637
user1803637

Reputation: 11

Reading CSV into C Struct

I'm trying to read a CSV into a struct in C. I need to read in an item name, its weight and its value. They are listed straight through in the CSV i.g (name, name, name, ..., weight, weight, ..., value, value, ..).

#include <stdio.h>

typedef struct {
    char name[25];
    double weight;
    double value;
} Item;

int main() {

    // Open data.csv
    FILE *packingList;
    packingList = fopen("data.csv", "r");

    // Check for proper file Opening
    if (packingList == NULL) {
        printf("File did not open correctly.\n");
        return 1; // Signal error
    }

    // Declare array of items to store data
    Item items[100];

    for (int i = 0; i < 3; i++)
    {
        fscanf(packingList,
               "%24[^,]",
               items[i].name);
        printf("| %s | ", items[i].name);
    }

    fclose(packingList);


    return 0;
}

csv

MRE1,MRE2,MRE3,MRE4,PolyPro,HygieneKit,PoopyWipes,Gloves,ExtraSocks1,ExtraSocks2,ExtraSocks3,Snacks,JetBoil,Ammo1,Ammo2,M16,SleepingBag,BivvySack,GoreTex,E-Tool,Canteens,FlakJacket,FrontSAPIPlate,BackSAPIPlate,WarBelt,Compass,NVG,Bayonet,Diary,Iodine,Knife,Lamp,Nameplate,Ointment,Quiver,Radio,Toothbrush,Utensils,Vitamins,X-acto Knife,Yellow safety belt,Zinc-oxide
2.125,2,1.875,2,1.5,2.5,0.5,0.5,0.25,0.25,0.25,1.5,1,3,3.75,7,5,2.5,2.5,3,4.25,3,8.25,8,3.5,1.125,1.125,5,0.75,0.375,0.875,1.125,0.25,0.875,3.75,7.625,0.375,0.25,0.125,3.75,2.125,4.625
12,10,8,6,10,5,6.5,4.5,9,6,2,1.5,5,15,100,80,35,22,18,12,18,30,50,25,12,50,3,86,50,6,9,5,1,8,7,20,5,2,1,5,2,6

The code I'm running only reads in the first first 3 items as a test, but only properly gets the first item name, the other 2 come out as weird characters. How would I go about writing this to properly read the CSV and attach the appropriate weight and values to each item?

My Output

| MRE1 | | $5�� | | �¤ | 

Upvotes: 1

Views: 71

Answers (1)

chux
chux

Reputation: 154601

Consider reading the 3 lines using fgets() and then parse the lines. Unless 3 lines read, no point in parsing.

Then parse the 3 lines, one at a time or as a group, as shown below.

This sample code uses "%n" to detect the offset of the scan, if it got that far. Notice that the ',' is in the format.

Other code could use strtod(), strcspn(), strtok(), for parsing.

#define ITEM_N 100
#define NAME_SIZE 25

// Scale the max lines size in some fashion.
// I recommend 2x the maximum expected size.
#define LINE_SIZE ((ITEM_N * NAME_SIZE + 3)*2)

char lines[3][LINE_SIZE];
char *s[3];
int i;
for (i = 0; i < 3; i++) {
  if (fgets(lines[i], sizeof lines[i], packingList) == NULL) {
    break;
  }
  s[i] = lines[i];
}

if (i == 3) {
  Item items[ITEM_N];
  size_t index;
  for (index = 0; index < ITEM_N; index++) {
    int n = 0;
    sscanf(s[0], "%24[^,]%*[,\n]%n", items[index].name, &n);
    if (n == 0) break;
    s[0] += n;

    n = 0;
    sscanf(s[1], "%lf%*[,\n]%n", &items[index].weight, &n);
    if (n == 0) break;
    s[1] += n;

    // likewise for .value (not shown)

  }
  
  // Code can now use `index` items.
}

Upvotes: 1

Related Questions