1

I'm trying to make a program that reads a csv file that looks like this:

name,hp,damage

duck,20,5

cat,30,10

giraffe,100,20

And I want to print the above csv file like this:

name:duck hp:20 damage:5

name:cat hp:30 damage:10

name:giraffe hp:100 damage:20

The requirement is that the header part of the csv file (name,hp,damage) should be stored into an array called 'header' and when printing the csv file the array 'header' must be used.

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

typedef struct {
    char name[1000];
    int hp;
    int damage;
} Monster;

typedef struct {
    char header1[4];
    char header2[2];
    char header3[6];
} Header;
int main() 
{
    FILE* fp = fopen("entityData.csv", "r");
    if (!fp) {
        printf("Error opening file\n");
        return 1;
    }

    Monster monsters[100];
    int num_records = 0;

    char line[100];
    Header headerList[20];

    fgets(headerList, sizeof(headerList), fp); 
    char *hd1, *hd2, *hd3;
    hd1 = strtok(headerList, ",");
    strncpy(monsters[num_records].name, hd1, 20);
    hd2 = strtok(NULL, ",");
    hd3 = strtok(NULL, "\n");
    


    while (fgets(line, sizeof(line), fp))
    {
        char* token = strtok(line, ","); 
        strncpy(monsters[num_records].name, token, 20);

        token = strtok(NULL, ",");
        monsters[num_records].hp = atoi(token);

        token = strtok(NULL, ",");
        monsters[num_records].damage = atoi(token);

        num_records++;
    }

    for (int i = 0; i < num_records; i++) {
        printf("name:%s hp:%d damage:%d\n",
            monsters[i].name, monsters[i].hp, monsters[i].damage);
    }

    fclose(fp);
    return 0;
}

Here is my code so far. I believe that I was able to get each header values into hd1, hd2, and hd3, but I'm not sure how those can be put into an array and used later. I also know that the print part is wrong right now, but I wrote it like that just to test if the values of each headers are printing well.

Also, the original csv file is in Korean, but I translated them into English for simplicity. So the array sizes might not match what I'm trying to achieve.

Any help would be greatly appreciated!

Arctodus
  • 5,743
  • 3
  • 32
  • 44
Mr Cake
  • 11
  • 1
  • 1
    Your compiler is probablty giving you a few warnings (e.g. for this `fgets(headerList, sizeof(headerList), fp);`). You should understand and fix those. Hint - `fgets()` can't automatically put things into a structure for you, it just reads strings. – pmacfarlane May 14 '23 at 07:33

1 Answers1

1

You assume a fixed header order so the requirements make little senese to me (opposed to reading the header then use that information to map a column number to a struct member).

  1. Use symbolic constants instead of magic values. When you do that it becomes apparent that it's odd that NAME_LEN > LINE_LEN.

  2. (Partial fix) Don't read more than MONSTERS records to prevent a buffer overflow. I didn't but may want to generate a warning if there is more data than space in your array.

  3. Added error handling for strtok(). Each line is a natural synchronization point so it's easy to skip invalid lines.

  4. As the header and data lines are parsing the same way, only used differently. This also fixes the defect with the 3rd strtok() expecting a '\n' but the data line a ','.

  5. (Not fixed) Consider using strtol() instead of a atoi() so you can detect errors. atoi() returns 0 on error.

#define _POSIX_C_SOURCE 200809L // strdup()
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define LINE_LEN 100
#define MONSTERS 100
#define NAME_LEN 1000
#define VALUES 3

typedef struct {
    char name[NAME_LEN];
    int hp;
    int damage;
} Monster;

int main(void) {
    FILE* fp = fopen("entityData.csv", "r");
    if (!fp) {
        printf("Error opening file\n");
        return 1;
    }
    char *header[VALUES] = { 0 };
    Monster monsters[MONSTERS];
    int num_records = -1;
    for(; num_records < MONSTERS; num_records++) {
        char line[LINE_LEN];
        if(!fgets(line, sizeof(line), fp))
            break;
        char *name = strtok(line, ",");
        char *hp = strtok(NULL, ",");
        char *damage = strtok(NULL, "\n");
        if(!name || !hp || !damage) {
            fprintf(stderr, "strtok failed at \"%s\"\n", line);
            num_records--;
            continue;
        }
        // header
        if(num_records == -1) {
            header[0] = strdup(name);
            header[1] = strdup(hp);
            header[2] = strdup(damage);
            if(!header[0] || !header[1] || !header[2]) {
               fprintf(stderr, "strdup failed\n");
               return 1;
            }
            continue;
        }
        // data
        strncpy(monsters[num_records].name, name, NAME_LEN);
        if(LINE_LEN >= NAME_LEN)
            monsters[num_records].name[NAME_LEN - 1] = '\0';
        monsters[num_records].hp = atoi(hp);
        monsters[num_records].damage = atoi(damage);
    }
    for (int i = 0; i < num_records; i++) {
        printf("%s:%s %s:%d %s:%d\n",
            header[0],
            monsters[i].name,
            header[1],
            monsters[i].hp,
            header[2],
            monsters[i].damage);
    }
    fclose(fp);
    for(int i = 0; i < VALUES; i++)
        free(header[i]);
}

and example session:

name:duck hp:20 damage:5
name:cat hp:30 damage:10
name:giraffe hp:100 damage:20
Harith
  • 4,663
  • 1
  • 5
  • 20
Allan Wind
  • 23,068
  • 5
  • 28
  • 38
  • Hi, I've noticed that the strdup() does not work on C language, even with the very first #define included. Is there a way to not use strdup()? – Mr Cake May 14 '23 at 12:43
  • @Mr Cake How does it ***not work***? – Harith May 14 '23 at 14:07
  • @Haris An error comes out, which states the following: Error C4996 'strdup': The POSIX name for this item is deprecated. Instead, use the ISO C and C++ conformant name: _strdup. See online help for details. – Mr Cake May 14 '23 at 14:18
  • @MrCake See https://stackoverflow.com/q/7582394/20017547 – Harith May 14 '23 at 14:48
  • @MrCake Sorry, I don't use Windows. Were you able to the `_strdup()` to work? – Allan Wind May 14 '23 at 17:53
  • @MrCake Please accept answer by clicking the check mark next to it if yo are all set. – Allan Wind May 18 '23 at 02:20