You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C语言读取CSV到结构体:如何正确读取数据并匹配重量与值?

解决CSV数据读取到C语言Item结构体的问题

首先明确你的需求:把CSV中三行数据(名称、重量、价值)对应到Item结构体数组,每个结构体存储对应物品的三个属性。你遇到的后续名称乱码,通常是字符串内存管理不当或CSV字段分割逻辑错误导致的,以下是完整的实现方案:

1. 定义核心结构体与辅助函数

首先定义Item结构体,同时实现一个CSV字段分割函数,用来把每行的逗号分隔字符串拆分成字符串数组:

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

// 定义Item结构体
typedef struct {
    char *name;    // 物品名称(动态分配内存)
    int weight;    // 重量,若需浮点数可改为float/double
    int value;     // 价值,同理可修改类型
} Item;

// 分割逗号分隔的字符串,返回字符串数组,数组最后一个元素为NULL
char** split_csv_line(const char *line) {
    char **tokens = NULL;
    int token_count = 0;
    char *line_copy = strdup(line); // 复制原字符串避免修改输入
    char *token = strtok(line_copy, ",");

    while (token != NULL) {
        // 去除字符串前后的空格(如果CSV有空格的话)
        char *trimmed = token;
        while (*trimmed == ' ') trimmed++;
        char *end = trimmed + strlen(trimmed) - 1;
        while (end > trimmed && *end == ' ') end--;
        *(end + 1) = '\0';

        // 扩容数组并存储分割后的字符串
        tokens = realloc(tokens, sizeof(char*) * (token_count + 2));
        tokens[token_count] = strdup(trimmed);
        token_count++;
        token = strtok(NULL, ",");
    }
    tokens[token_count] = NULL; // 用NULL标记数组结束
    free(line_copy);
    return tokens;
}

2. 读取CSV并关联数据

核心逻辑是:读取CSV的三行,分别拆分后,按索引一一对应到Item结构体数组中,注意每个name都要动态分配内存,避免临时缓冲区失效导致乱码:

// 读取CSV文件并返回Item数组,同时通过item_count返回物品数量
Item* read_csv_items(const char *filename, int *item_count) {
    FILE *fp = fopen(filename, "r");
    if (!fp) {
        perror("Failed to open file");
        return NULL;
    }

    char line[1024];
    char **names = NULL, **weights_str = NULL, **values_str = NULL;

    // 读取第一行:物品名称
    if (!fgets(line, sizeof(line), fp)) goto cleanup;
    // 去除换行符
    line[strcspn(line, "\n")] = '\0';
    names = split_csv_line(line);

    // 读取第二行:重量
    if (!fgets(line, sizeof(line), fp)) goto cleanup;
    line[strcspn(line, "\n")] = '\0';
    weights_str = split_csv_line(line);

    // 读取第三行:价值
    if (!fgets(line, sizeof(line), fp)) goto cleanup;
    line[strcspn(line, "\n")] = '\0';
    values_str = split_csv_line(line);

    // 统计物品数量(以名称行的字段数为准)
    *item_count = 0;
    while (names[*item_count] != NULL) (*item_count)++;

    // 检查三行的字段数量是否一致
    int weight_count = 0, value_count = 0;
    while (weights_str[weight_count] != NULL) weight_count++;
    while (values_str[value_count] != NULL) value_count++;
    if (weight_count != *item_count || value_count != *item_count) {
        fprintf(stderr, "CSV字段数量不匹配\n");
        goto cleanup;
    }

    // 分配Item数组内存
    Item *items = malloc(sizeof(Item) * (*item_count));
    if (!items) goto cleanup;

    // 填充每个Item的属性
    for (int i = 0; i < *item_count; i++) {
        items[i].name = names[i]; // 直接复用split分配的内存
        items[i].weight = atoi(weights_str[i]);
        items[i].value = atoi(values_str[i]);
    }

    // 释放临时的字符串数组(names的内存已经转移给items,不用释放)
    free(weights_str);
    free(values_str);
    fclose(fp);
    return items;

cleanup:
    // 错误处理:释放所有已分配的内存
    if (names) {
        for (int i = 0; names[i]; i++) free(names[i]);
        free(names);
    }
    if (weights_str) {
        for (int i = 0; weights_str[i]; i++) free(weights_str[i]);
        free(weights_str);
    }
    if (values_str) {
        for (int i = 0; values_str[i]; i++) free(values_str[i]);
        free(values_str);
    }
    fclose(fp);
    *item_count = 0;
    return NULL;
}

3. 测试与内存释放

使用示例和内存清理函数:

// 释放Item数组的内存
void free_items(Item *items, int item_count) {
    for (int i = 0; i < item_count; i++) {
        free(items[i].name);
    }
    free(items);
}

int main() {
    int item_count;
    Item *items = read_csv_items("items.csv", &item_count);
    if (!items) return 1;

    // 打印读取结果
    printf("读取到%d个物品:\n", item_count);
    for (int i = 0; i < item_count; i++) {
        printf("物品%d:名称=%s,重量=%d,价值=%d\n", 
               i+1, items[i].name, items[i].weight, items[i].value);
    }

    // 释放内存
    free_items(items, item_count);
    return 0;
}

关键注意点(解决乱码的核心)

  • 动态内存分配:每个物品名称用strdup分配独立内存,避免指向栈上的临时缓冲区(栈内存函数结束后会被覆盖,导致后续读取乱码)。
  • CSV字段分割:用strtok拆分时,必须复制原字符串(strdup(line)),因为strtok会修改原字符串。
  • 字段数量校验:确保三行的字段数一致,避免数组越界访问。
  • 换行符处理:用strcspn去除每行末尾的换行符,避免把换行符当成字符串的一部分。

内容的提问来源于stack exchange,提问作者user1803637

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 23:00:26