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
相关产品推荐
相关产品推荐

