如何在C语言中将CSV文件数据读取到结构体数组中?
问题:CSV数据无法正确读取到结构体数组中
我正尝试将CSV文件中的数据读取到结构体数组中,程序已计算出所需结构体的数量并分配了足够的内存,但将数据扫描到数组的操作无法正常执行。相关代码片段如下:
#include <stdio.h> #include <stdlib.h> #include <string.h> typedef struct{ char *date; double open; double high; double low; double close; double volume; double adjclose; } DATA; DATA *table, *highest_table; int main(int argc, char *argv[]) { // 省略了计算行数和分配内存的代码 // 以下是尝试读取数据的逻辑(示例) FILE *fp = fopen("data.csv", "r"); if (!fp) { /* 简单错误处理 */ } for (int i = 0; i < count; i++) { fscanf(fp, "%s,%lf,%lf,%lf,%lf,%lf,%lf", table[i].date, &table[i].open, &table[i].high, &table[i].low, &table[i].close, &table[i].volume, &table[i].adjclose); } }
核心问题分析
扫描失败大概率是两个关键问题导致的:
char *date是野指针:结构体里的date只是一个未初始化的指针,没有分配实际内存空间,直接用fscanf写入会触发内存访问错误,要么崩溃要么数据乱码。fscanf格式字符串不匹配CSV规则:如果日期是2024-05-20这类带特殊字符的字符串,%s会在遇到逗号前停止读取,但如果格式字符串没处理好分隔逻辑,或者CSV有表头没跳过,会直接导致后续数值读取错位。
修复后的完整代码
下面是解决了内存、格式匹配和错误处理问题的完整代码:
#include <stdio.h> #include <stdlib.h> #include <string.h> typedef struct{ char *date; double open; double high; double low; double close; double volume; double adjclose; } DATA; DATA *table; int main(int argc, char *argv[]) { // 1. 打开CSV文件并检查 FILE *fp = fopen("data.csv", "r"); if (!fp) { perror("Failed to open CSV file"); return EXIT_FAILURE; } // 2. 统计有效数据行数(跳过表头) char buffer[256]; int data_count = 0; // 先跳过第一行表头 if (!fgets(buffer, sizeof(buffer), fp)) { fclose(fp); perror("Empty CSV file"); return EXIT_FAILURE; } // 统计剩余数据行数 while (fgets(buffer, sizeof(buffer), fp)) { data_count++; } // 重置文件指针到开头,再次跳过表头 fseek(fp, 0, SEEK_SET); fgets(buffer, sizeof(buffer), fp); // 3. 分配结构体数组内存并检查 table = malloc(data_count * sizeof(DATA)); if (!table) { perror("Failed to allocate memory for struct array"); fclose(fp); return EXIT_FAILURE; } // 4. 读取数据到结构体数组 int i = 0; char date_temp[20]; // 临时存储日期,避免野指针 while (fscanf(fp, "%[^,],%lf,%lf,%lf,%lf,%lf,%lf", date_temp, &table[i].open, &table[i].high, &table[i].low, &table[i].close, &table[i].volume, &table[i].adjclose) == 7) { // 为date字段分配独立内存并复制内容 table[i].date = strdup(date_temp); if (!table[i].date) { perror("Failed to allocate memory for date string"); // 出错时清理已分配资源,避免内存泄漏 for (int j = 0; j < i; j++) { free(table[j].date); } free(table); fclose(fp); return EXIT_FAILURE; } i++; } // 5. 测试输出(可选) printf("Successfully read %d data entries:\n", data_count); for (int j = 0; j < data_count; j++) { printf("Entry %d: Date=%s, Open=%.2lf\n", j+1, table[j].date, table[j].open); free(table[j].date); // 先释放date字段的内存 } free(table); // 再释放结构体数组内存 fclose(fp); return EXIT_SUCCESS; }
关键修复点说明
- 解决野指针问题:用临时缓冲区
date_temp先读取日期,再通过strdup为table[i].date分配独立内存,确保写入有合法的存储空间。 - 匹配CSV格式:
%[^,]表示读取所有字符直到遇到逗号,完美适配CSV的逗号分隔规则,保证日期字符串被完整读取。 - 跳过表头:CSV通常第一行是表头,必须跳过否则会把表头内容读入结构体,导致后续数值读取错位。
- 完善错误处理:每一步操作(文件打开、内存分配、数据读取)都添加了错误检查,出错时会清理已分配的资源,避免内存泄漏。
- 正确释放内存:使用完结构体数组后,要先逐个释放
date字段的内存,再释放整个数组的内存,避免内存泄漏。
内容的提问来源于stack exchange,提问作者reeseboy07




