Blarsssss
Blarsssss

Reputation: 101

Putting values into a struct from a file

Okay so I know how to put values into a struct from a file.

My values in the file are seperated my commas. The data is like this:

number, product, price, other

Only some items have the other value!

How do I have an optional value in a struct?

such:

typedef struct stockItem {
    int number;
    char* product;
    int price;
    char *other;
} stockItem;

I'm inputting the data something like this:

while (fgets(str, 255, invf) != NULL){   
    int numOfItems = atoi(strtok(str, " ,"));
    char *stockCode = strtok(NULL, " ,");
    int price = atoi(strtok(NULL, " ,"));
    char *other = strTok(NULL, " "));

    stockItem *item = stockItem_new(numOfItems , stockCode, price, other);

It of course doesn't execute because some items have the other, what should i do? should i insert null into every value that doesn't have an other ?

Upvotes: 0

Views: 93

Answers (1)

Crowman
Crowman

Reputation: 25908

Yes, you should set other to NULL for every item that doesn't have that field. This is the normal way to indicate the absence of something pointed to.

Also, strtok() will return an internal pointer to str, which you reuse for every item. You should ensure that your stockItem_new() function is actually copying that data (using strdup() or strcpy()) and not just setting that field equal to the pointer, or this field in all your structs will end up pointing to the same memory, which will change each time you call fgets(), and will probably cease to exist altogether after your reading function returns.

Here's some sample code:

#define _POSIX_C_SOURCE 200809L

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

#define BUFFER_SIZE 256

struct stock_item {
    int number;
    char * product;
    int price;
    char * other;
};

struct stock_item * stock_item_new(const int num_items,
                                   const char * stock_code,
                                   const int price,
                                   const char * other);
void stock_item_destroy(struct stock_item * item);
void stock_item_print(struct stock_item * item);

int main(void)
{
    FILE * fp = fopen("data.txt", "r");
    if ( !fp ) {
        perror("couldn't open data file");
        return EXIT_FAILURE;
    }

    char buffer[BUFFER_SIZE];
    const char * delim = " ,\n";
    while ( fgets(buffer, BUFFER_SIZE, fp) ) {
        int num_items = atoi(strtok(buffer, delim));
        char * stock_code = strtok(NULL, delim);
        int price = atoi(strtok(NULL, delim));
        char * other = strtok(NULL, delim);

        struct stock_item * new_item;
        new_item = stock_item_new(num_items, stock_code, price, other);

        stock_item_print(new_item);

        stock_item_destroy(new_item);
    }

    fclose(fp);

    return 0;
}

struct stock_item * stock_item_new(const int num_items,
                                   const char * stock_code,
                                   const int price,
                                   const char * other)
{
    struct stock_item * new_item = malloc(sizeof *new_item);
    if ( !new_item ) {
        perror("couldn't allocate memory for stock item");
        exit(EXIT_FAILURE);
    }

    new_item->number = num_items;
    new_item->price = price;

    new_item->product = strdup(stock_code);
    if ( !new_item->product ) {
        perror("couldn't allocate memory for product name");
        exit(EXIT_FAILURE);
    }

    if ( other ) {
        new_item->other = strdup(other);
        if ( !new_item->other ) {
            perror("couldn't allocate memory for 'other' field");
            exit(EXIT_FAILURE);
        }
    }
    else {
        new_item->other = NULL;
    }

    return new_item;
}

void stock_item_destroy(struct stock_item * item)
{
    free(item->product);
    free(item->other);
    free(item);
}

void stock_item_print(struct stock_item * item)
{
    printf("%d, %s, %d, %s\n", item->number, item->product,
            item->price, item->other ? item->other : "(none)");
}

which, when run with the following data file:

paul@horus:~/src/sandbox/itemfile$ cat data.txt
20, rifle, 99
33, bucket, 30, plastic
50, fish, 5, gold
12, hammer, 45, left-handed
9, backscratcher, 13
paul@horus:~/src/sandbox/itemfile$ 

produces the following output:

paul@horus:~/src/sandbox/itemfile$ ./itemfile
20, rifle, 99, (none)
33, bucket, 30, plastic
50, fish, 5, gold
12, hammer, 45, left-handed
9, backscratcher, 13, (none)
paul@horus:~/src/sandbox/itemfile$ 

The stock_item_print() function checks whether the other field is NULL, and if it is, outputs "(none)". Otherwise it prints it normally.

Also note that passing NULL to free() is fine, so we don't have to check the other field in the stock_item_destroy() function.

Finally, I haven't made any changes to it (other than adding '\n' to the list of delimiters), but your parsing code with strtok() is very fragile, and should be expanded to include a great deal more error checking. For instance, strtol() is better than atoi(), and you should be checking each time whether strtok() returns NULL.

Upvotes: 3

Related Questions