C 将值从文件放入结构中

C 将值从文件放入结构中,c,struct,C,Struct,好的,我知道如何将值从文件放入结构中 文件中的值与逗号分开。数据如下: number, product, price, other 只有某些项目具有其他值 如何在结构中具有可选值 例如: typedef struct stockItem { int number; char* product; int price; char *other; } stockItem; while (fgets(str, 255, invf) != NULL){ i

好的,我知道如何将值从文件放入结构中

文件中的值与逗号分开。数据如下:

number, product, price, other
只有某些项目具有其他值

如何在结构中具有可选值

例如:

typedef struct stockItem {
    int number;
    char* product;
    int price;
    char *other;
} stockItem;
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);
我输入的数据如下:

typedef struct stockItem {
    int number;
    char* product;
    int price;
    char *other;
} stockItem;
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);

它当然不会执行,因为有些项目有另一个,我应该怎么做?我应该在每个没有其他字段的值中插入null吗?

是的,对于每个没有该字段的项,应该将
other
设置为
null
。这是表示没有指向某个对象的正常方式

另外,
strtok()
将返回一个指向
str
的内部指针,您可以对每个项目重复使用该指针。您应该确保
stockItem_new()
函数实际上是在复制该数据(使用
strdup()
strcpy()
),而不仅仅是将该字段设置为指针,否则所有
struct
中的该字段都将指向相同的内存,每次调用
fgets()时都会发生变化
,并且在读取函数返回后可能会完全不存在

下面是一些示例代码:

#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)");
}
生成以下输出:

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$ 
stock\u item\u print()
函数检查
other
字段是否为
NULL
,如果为空,则输出
(无)
。否则它会正常打印

还要注意,将
NULL
传递到
free()
是可以的,因此我们不必检查
stock\u item\u destroy()函数中的
other
字段


最后,我没有对它做任何更改(除了在分隔符列表中添加
'\n'
),但是您使用
strtok()
解析代码非常脆弱,应该扩展以包含更多的错误检查。例如,
strtol()
优于
atoi()
,每次
strtok()
返回
NULL

是的,对于没有该字段的每个项目,都应该将
other
设置为
NULL
。这是表示没有指向某个对象的正常方式

另外,
strtok()
将返回一个指向
str
的内部指针,您可以对每个项目重复使用该指针。您应该确保
stockItem_new()
函数实际上是在复制该数据(使用
strdup()
strcpy()
),而不仅仅是将该字段设置为指针,否则所有
struct
中的该字段都将指向相同的内存,每次调用
fgets()时都会发生变化
,并且在读取函数返回后可能会完全不存在

下面是一些示例代码:

#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)");
}
生成以下输出:

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$ 
stock\u item\u print()
函数检查
other
字段是否为
NULL
,如果为空,则输出
(无)
。否则它会正常打印

还要注意,将
NULL
传递到
free()
是可以的,因此我们不必检查
stock\u item\u destroy()函数中的
other
字段


最后,我没有对它做任何更改(除了在分隔符列表中添加
'\n'
),但是您使用
strtok()
解析代码非常脆弱,应该扩展以包含更多的错误检查。例如,
strtol()
atoi()
好,每次你都应该检查
strtok()
是否返回
NULL

我已经确保在stockItem_new()中使用了strdup,所以在我的结构中,我是将值更改为NULL还是在循环中执行?最简单的方法是在循环中执行。然后
stockItem\u new()
可以检查传递给它的
other
的值是否为
NULL
。如果是,它可以将该字段设置为
NULL
。如果不是,那么它可以复制它,就像使用
stockCode
一样。因此,如果我在循环中执行此操作,我是否必须在每次循环后将其设置为NULL,否则该值将无法继续执行?@blarssss:请稍等,我会写出一些代码。我已确保在stockItem_new()中使用了strdup,所以在我的结构中,我会将值更改为NULL还是在循环中执行?最简单的方法是在循环中执行。然后
stockItem\u new()
可以检查传递给它的
other
的值是否为
NULL
。如果是,它可以将该字段设置为
NULL
。如果不是,那么它可以复制它,就像
stockCode
一样。因此,如果我在循环中执行此操作,我是否必须在每次循环后将其设置为NULL,否则该值不会继续执行?@blarssss:请稍等,我会写出一些代码。