从C中的文本文件中读取CSV

dan*_*nze 3 c

我正在尝试从C中的文本文件中读取CSV.文本文件格式为

1,Bob,bob@gmail.com
2,Daniel,daniel@gmail.com
3,John,john@gmail.com
Run Code Online (Sandbox Code Playgroud)

当我运行程序时,数字显示正常,但名称和电子邮件显示为垃圾.这是我的计划......

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

typedef struct {
    int number;
    char* name;
    char* email;
} Owner;

Owner owners[100];

int load(char* filename)
{
    char buffer[200];
    char token[50];
    Owner* owner;
    int owners_size = 0;
    FILE* file = fopen(filename, "r");

    while(fgets(buffer, 200, file) != NULL)
    {
        owner = (Owner*)malloc(sizeof(Owner));
        owner->number = atoi(strtok(buffer, ","));
        owner->name = strtok(NULL, ",");
        owner->email = strtok(NULL, ",");
        owners[owners_size++] = *owner;
    }

    fclose(file);
    return owners_size;
}

int main()
{
    int choise, owners_size, index;
    char* owners_filename = "owners2.txt";

    owners_size = load(owners_filename);

    if(owners_size)
    {
        printf("owners size: %d\n\n", owners_size);

        for(index = 0; index < owners_size; index++)
            printf("%d, %s %s\n", owners[index].number, owners[index].name, owners[index].email);
    }
}
Run Code Online (Sandbox Code Playgroud)

谁能告诉我原因是什么.我感谢您的帮助.

Jon*_*ler 5

两个问题:

  1. 您没有为结构中的字符串分配空间:

    typedef struct
    {
        int   number;
        char *name;
        char *email;
    } Owner;
    
    Run Code Online (Sandbox Code Playgroud)

    您需要为指向这些指针的指针提供空间来保存名称.

  2. 您继续提供指向缓冲区的指针,该指针将重复用于每行输入:

    while(fgets(buffer, 200, file) != NULL)
    {
        owner = (Owner*)malloc(sizeof(Owner));
        owner->number = atoi(strtok(buffer, ","));
        owner->name = strtok(NULL, ",");
        owner->email = strtok(NULL, ",");
        owners[owners_size++] = *owner;
    }
    
    Run Code Online (Sandbox Code Playgroud)

    第一行存储为缓冲区中的一些指针.然后下一行覆盖缓冲区并再次切断该行,遍历原始输入.

考虑使用strdup():

while (fgets(buffer, 200, file) != NULL)
{
    owner = (Owner *)malloc(sizeof(Owner));
    owner->number = atoi(strtok(buffer, ","));
    owner->name = strdup(strtok(NULL, ","));
    owner->email = strdup(strtok(NULL, ","));
    owners[owners_size++] = *owner;
}
Run Code Online (Sandbox Code Playgroud)

这是稍微有点危险的代码(我不会在生产代码中使用它)因为它没有检查strtok()在预期时找到了一个令牌(或者strdup()是成功的).再说一遍,我也不会strtok()在生产代码中使用; 我会使用POSIX strtok_r()或Microsoft的,strtok_s()如果它们可用,或者一些替代技术,可能使用strspn()strcspn().如果strdup()没有,您可以使用相同或不同的名称编写自己的名称:

char *strdup(const char *str)
{
    size_t len = strlen(str) + 1;
    char *dup = malloc(len);
    if (dup != 0)
        memmove(dup, str, len);  // Or memcpy() - that is safe in this context
    return(dup);
}
Run Code Online (Sandbox Code Playgroud)

您可能会注意到您的代码仅适用于简单的CSV文件.如果您遇到这样的行(这是合法的CSV),您就会遇到问题(您的值中带引号,并且由于引用字符串中的逗号而导致错误分裂):

1,"Bob ""The King"" King","Bob King, Itinerant Programmer <bob@gmail.com>"
Run Code Online (Sandbox Code Playgroud)