将CSV读取到结构体数组时遇段错误及解析失败问题求助
问题修复方案
核心问题分析
你的代码存在两个致命问题,直接导致段错误和解析失败:
- 野指针未初始化:
char *line;是未分配内存的野指针,fgets尝试向非法地址写入数据触发段错误。- 结构体中的
name、dept是未初始化的指针,sscanf无法向有效内存写入字符串,进一步引发内存混乱。
- 解析逻辑依赖非法内存:即使格式串正确,野指针导致的内存错误会让解析行为完全不可预测。
修复后的完整代码
方案一:使用固定大小数组(简单直接,适合已知字段长度场景)
#define MAXROW 30 #define MAX_STR_LEN 30 // 定义字符串字段的最大长度 #define LINE_BUF_SIZE 256 // 每行CSV的最大缓冲区 typedef struct emp_info { int emp_id; char name[MAX_STR_LEN]; // 替换指针为固定数组 char dept[MAX_STR_LEN]; int salary; } emp_info; void store(emp_info *data, char *filename); int main(int argc, char **argv) { emp_info data[MAXROW]; char *filename = "dat/emp_info.csv"; store(data, filename); return 0; } void store(emp_info emp_arr[], char *filename) { size_t n = 0; char line[LINE_BUF_SIZE]; // 分配足够的栈内存存储每行数据 FILE *csv_stream; if ((csv_stream = fopen(filename, "r")) == NULL) { fprintf(stderr, "file open for reading failed. exit!\n"); exit(EXIT_FAILURE); } while (n < MAXROW && fgets(line, sizeof(line), csv_stream) != NULL) { // 去掉行尾的换行符(如果存在),避免干扰解析 line[strcspn(line, "\n")] = '\0'; emp_info tmp; // 使用%s匹配字符串,结合数组长度限制避免溢出 if (sscanf(line, "%d,%29[^,],%29[^,],%d", &tmp.emp_id, tmp.name, tmp.dept, &tmp.salary) != 4) { fprintf(stderr, "error: failed to parse line %zu.\n", n+1); exit(EXIT_FAILURE); } emp_arr[n++] = tmp; } if (fclose(csv_stream) == EOF) perror("stream error-csv_stream."); }
方案二:动态分配内存(灵活适配可变长度字段)
如果需要支持更长的字符串字段,可以用动态分配内存:
#define MAXROW 30 #define LINE_BUF_SIZE 256 typedef struct emp_info { int emp_id; char *name; char *dept; int salary; } emp_info; void store(emp_info *data, char *filename); void free_emp_data(emp_info *data, size_t count); // 新增释放内存函数 int main(int argc, char **argv) { emp_info data[MAXROW]; char *filename = "dat/emp_info.csv"; store(data, filename); // 使用完数据后记得释放内存 free_emp_data(data, MAXROW); return 0; } void store(emp_info emp_arr[], char *filename) { size_t n = 0; char line[LINE_BUF_SIZE]; FILE *csv_stream; if ((csv_stream = fopen(filename, "r")) == NULL) { fprintf(stderr, "file open for reading failed. exit!\n"); exit(EXIT_FAILURE); } while (n < MAXROW && fgets(line, sizeof(line), csv_stream) != NULL) { line[strcspn(line, "\n")] = '\0'; emp_info tmp; char name_buf[LINE_BUF_SIZE], dept_buf[LINE_BUF_SIZE]; // 先读取到临时缓冲区,再动态分配内存 if (sscanf(line, "%d,%[^,],%[^,],%d", &tmp.emp_id, name_buf, dept_buf, &tmp.salary) != 4) { fprintf(stderr, "error: failed to parse line %zu.\n", n+1); exit(EXIT_FAILURE); } // 分配足够内存并复制字符串 tmp.name = malloc(strlen(name_buf) + 1); tmp.dept = malloc(strlen(dept_buf) + 1); if (!tmp.name || !tmp.dept) { fprintf(stderr, "memory allocation failed.\n"); exit(EXIT_FAILURE); } strcpy(tmp.name, name_buf); strcpy(tmp.dept, dept_buf); emp_arr[n++] = tmp; } if (fclose(csv_stream) == EOF) perror("stream error-csv_stream."); } void free_emp_data(emp_info *data, size_t count) { for (size_t i = 0; i < count; i++) { free(data[i].name); free(data[i].dept); } }
关键修改说明
- 修复野指针:
- 将
char *line改为栈数组char line[LINE_BUF_SIZE],确保有合法内存存储每行数据。 - 方案一中把结构体的指针成员改为固定大小数组,直接提供存储区域;方案二中先读取到临时缓冲区,再动态分配内存存储字符串。
- 将
- 优化解析逻辑:
- 使用
strcspn(line, "\n")去掉行尾的换行符,避免换行符干扰sscanf的匹配。 - 格式串中
%29[^,]限制读取的字符数,防止数组溢出(数组大小为30,留1个位置给终止符\0)。
- 使用
- 内存管理:方案二中新增了内存释放函数,避免内存泄漏。
内容的提问来源于stack exchange,提问作者jian
相关产品推荐
相关产品推荐

