Friday, October 11, 2019

Reading A CSV Into An Array of Structs In C

So one of my assignments for my C class involves creating a struct and populating it. Of course, I did this over a month ago and needed a challenge. One thing that I was trying to do is create a CSV of 802 Pokemon whose name, attack, special attack, defense, special defense, speed, HP, primary type, and secondary type are all then loaded into an array of structs. I tried using fscanf for this, but couldn't figure out how to tokenize it. I couldn't figure out how to use strtok either, so I decided today was the day I was going to figure out strtok.

Now I didn't quite write my Pokemon code just yet. That particular code I want to be able to run averages based on type, classify a Pokemon as pseudolegendary based on the sum of its base stats, etc. But this struct project acted as a proof of concept of the reading a CSV into a struct array.

The CSV is the following set of values.

Name,Developer,1,Genre1,Genre2,Perspective,1,1,DevEngine,
Tetris,Alexey Pajitnov,1984,Puzzle,Strategy,Static,1,1,Various,
Beat Saber,Beat Games,2018,Rhythm,Null,VR,0,0,Unity,
Portal,Valve,2011,Puzzle,Platformer,FPV,0,0,Source,
Portal 2,Valve,2007,Puzzle,Platformer,FPV,0,0,Source,
Fallout: New Vegas,Obsidian,2010,FPS,RPG,FPV,0,0,Gamebryo,
140,Carlsen Games,2013,Platformer,Rhythm,Sidescroller,0,0,Unity,
Antichamber,Demruth,2013,Puzzle,Platformer,FPV,0,0,Unreal,
The Stanley Parable,Galactic Cafe,2013,Interactive,Null,FPV,0,0,Source,
Transistor,Supergiant Games,2014,RPG,Turn-based,TPV,0,0,Custom,
VVVVVV,Terry Cavanagh,2010,Platformer,Null,Sidescroller,0,0,Flash,
Paper Mario: The Thousand Year Door,Nintendo,2004,RPG,Turn-based,TPV,0,0,custom,
Luigi's Mansion,Nintendo,2001,RPG,Null,TPV,0,0,custom,

The first line has the names of the categories except where there were integers, just because I didn't want to risk there being an error thrown if a string were attempted to be read into an integer (although I'd imagine that'd just read its ASCII value).

The game list isn't ordered (except for the first 4, where they're my top favorite games of all time), but they're all my faves list.

My struct is as follows:


typedef struct faveGames_struct {
    char name[40]; //Game Name
    char developer[20]; //Game Dev
    int release; //Release Year
    char genre1[20]; //Primary Genre
    char genre2[20]; //Secondary Genre
    char perspective[20]; //FPS, Sidescroller, TPV, Top-Down, etc.
    int multiplayer; //0 = isn't multiplayer, 1 = is multiplayer
    int online; //0 = local play, 1 = online play
    char devEngine[30]; //Unity, Unreal, Source (Valve), etc.
} Game;

So I explain in the following video how I implement strtok to tokenize my CSV and populate an array of structs of type Game.


My actual code is as follows. It requires that you have the CSV saved in the same directory with the title "favegames.csv" by pasting it into Notepad, and when you Save As, set it to "All Files" instead of just .txt, and just save it as favegames.csv.

Save the struct as structdef.h, or remove the include statement and paste it straight into the main C file.

Here's my tokenizer into an array of structs!

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

int j = 1;

int main()
{
    FILE *myFile;
    myFile = fopen("favegames.csv", "r");

    if (myFile == NULL){
        printf("File did not successfully open. \n");
    }

    rewind(myFile);

    char myLine[200];
    fgets(myLine,1024,myFile);
    char* delimiter = ",";

    int i = 0;

    for (char c = getc(myFile); c != EOF; c=getc(myFile)){
        if (c == '\n'){
            j++;
        }
    }

    Game games[j+1];
    rewind(myFile);

     while(fgets(myLine,200, (FILE*)myFile) != NULL ){

        strcpy(games[i].name,strtok(myLine, delimiter));

        strcpy(games[i].developer, strtok(NULL, delimiter));


        games[i].release = atoi(strtok(NULL, delimiter));

        strcpy(games[i].genre1, strtok(NULL, delimiter));

        strcpy(games[i].genre2, strtok(NULL, delimiter));

        strcpy(games[i].perspective, strtok(NULL, delimiter));

        games[i].multiplayer = atoi(strtok(NULL, delimiter));

        games[i].online = atoi(strtok(NULL, delimiter));

        strcpy(games[i].devEngine, strtok(NULL, delimiter));

        i++;
    }

    fclose(myFile);

    FILE* binStruct = NULL;

    binStruct = fopen("binStruct.bin", "wb");
    if (binStruct == NULL){
        printf("Couldn't open binStruct.\n");
    }

    fwrite(games,sizeof(Game),j,binStruct);
    fclose(binStruct);

    searchStruct (games);
}

void searchStruct(Game games[j]){
char gameName[40];
printf("Enter a video game: ");
fgets(gameName,40,stdin);
int len = strlen(gameName);

if (gameName[len-1] == '\n'){
    gameName[len-1] = 0;
}


int i = 0;

for (int a = 0; a <= j; a++){
    if(strcmp(gameName,games[a].name) == 0){
       break;
    }
    else {
        if(a == j){
            printf("Invalid entry.");
        return;
        }
    }
    i++;

}


    printf("%s was made in %d by %s.\n", games[i].name, games[i].release, games[i].developer);
    if (strcmp(games[i].genre2, "Null") == 0){
        printf("It is a %s game.\n", games[i].genre1);
    }
    else {
        printf("It is a %s and %s game.\n", games[i].genre1, games[i].genre2);
    }
    if (strcmp(games[i].devEngine, "custom") == 0){
        printf("It was made using a custom-built game engine.\n");
    }
    else {
        printf("It was made using the %s game engine.\n", games[i].devEngine);
    }
    printf("It has a %s type of perspective.\n", games[i].perspective);
    if (games[i].multiplayer == 1){
        printf("It has multiplayer capabilities and ");
    }
    else {
        printf("It does not have multiplayer capabilities and ");
    }
    if (games[i].online == 1){
        printf("has online capabilities.\n\n");
    }
    else {
        printf("does not have online capabilities.\n\n");
    }
}

No comments:

Post a Comment