我正在尝试制作一个程序,该程序从csv文件中获取数据/值并打印出来。但我在某些方面很挣扎

I am trying to make a program that takes data/values from a csv file and print it. But I am struggling with some parts

提问人:Mr Cake 提问时间:5/14/2023 最后编辑:ArctodusMr Cake 更新时间:5/14/2023 访问量:64

问:

我正在尝试制作一个读取csv文件的程序,如下所示:

name,hp,damage

duck,20,5

cat,30,10

giraffe,100,20

我想像这样打印上面的csv文件:

name:duck hp:20 damage:5

name:cat hp:30 damage:10

name:giraffe hp:100 damage:20

要求是 csv 文件的标头部分(name、hp、damage)应存储到名为“header”的数组中,并且在打印 csv 文件时必须使用数组“header”。

#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;
}

这是我到目前为止的代码。我相信我能够将每个标头值放入 hd1、hd2 和 hd3 中,但我不确定如何将它们放入数组中并在以后使用。我也知道打印部分现在是错误的,但我这样写只是为了测试每个标题的值是否打印良好。

此外,原始 csv 文件是韩文的,但为了简单起见,我将它们翻译成了英文。因此,数组大小可能与我试图实现的目标不匹配。

任何帮助将不胜感激!

c csv 解析

评论

1赞 pmacfarlane 5/14/2023
你的编译器可能会给你一些警告(例如,对于这个)。您应该了解并解决这些问题。提示 - 不能自动将内容放入结构中,它只是读取字符串。fgets(headerList, sizeof(headerList), fp);fgets()

答:

1赞 Allan Wind 5/14/2023 #1

您假设标题顺序是固定的,因此这些要求对我来说意义不大(而不是读取标题,然后使用该信息将列号映射到结构成员)。

  1. 使用符号常量而不是魔术值。当你这样做时,很明显,> .NAME_LENLINE_LEN

  2. (部分修复)不要读取超过记录,以防止缓冲区溢出。我没有,但如果数组中的数据多于空间,则可能希望生成警告。MONSTERS

  3. 添加了错误处理。每一行都是一个自然的同步点,因此很容易跳过无效的行。strtok()

  4. 由于标头和数据线的解析方式相同,只是使用方式不同。这也修复了第 3 个期望 a 但数据线 a 的缺陷。strtok()'\n'','

  5. (不固定)请考虑使用 而不是 ,以便检测错误。 出错时返回 0。strtol()atoi()atoi()

#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]);
}

和示例会话:

name:duck hp:20 damage:5
name:cat hp:30 damage:10
name:giraffe hp:100 damage:20

评论

0赞 Mr Cake 5/14/2023
嗨,我注意到 strdup() 不适用于 C 语言,即使包含第一个 #define。有没有办法不使用 strdup()?
0赞 Harith 5/14/2023
@Mr蛋糕 它怎么不起作用
0赞 Mr Cake 5/14/2023
@Haris 出现错误,指出以下内容:错误 C4996“strdup”:此项的 POSIX 名称已弃用。请改用 ISO C 和C++符合标准的名称:_strdup。有关详细信息,请参阅联机帮助。
0赞 Harith 5/14/2023
@MrCake 查看 stackoverflow.com/q/7582394/20017547
0赞 Allan Wind 5/15/2023
@MrCake 对不起,我不使用 Windows。你能上班吗?_strdup()