C语言如何读取带表头CSV列数据并存储到结构体中
核心问题修正点
原代码存在以下问题导致无法正常读取:
- 结构体字段类型定义错误:分数为带小数的数值,原定义用
char单字符类型无法存储5.5这类浮点数,也不支持字符串拷贝操作 - 变量名不统一:定义了
field变量,实际逻辑中使用未声明的campoCount,无法通过编译 - 未处理表头行:第一行
Aluno;p1;p2是列名而非学生数据,直接存入结构体会导致第一条数据无效 - 字段计数器未逐行重置:每行分割后计数器没有归零,下一行的字段判断逻辑完全失效
- 缺失类型转换:分割得到的字符串内容没有转成对应数值类型,直接调用
strcpy向单字符变量拷贝内容会触发内存越界 - 函数未前置声明:
printAlunos定义在main函数之后,调用前无声明会触发编译错误 - 未处理行尾换行:
fgets读取的内容会携带行尾换行符,最后一个字段会被污染 - 存在缓冲区溢出风险:文件名、姓名字段的读取没有预留字符串结束符的存储空间
完整可运行代码
#include <stdio.h> #include <stdlib.h> #include <string.h> typedef struct aluno { char nome[10]; float p1; float p2; } dict; // 函数前置声明 void printAlunos(dict aluno[], int count); int main() { FILE *fp; dict aluno[9]; char nomeArq[10]; char buffer[1024]; int i = 0; printf("Enter the file name: "); // 留1字节空间存字符串结束符,避免溢出 scanf("%9[^\n]", nomeArq); // 清空输入缓冲区残留的换行符,避免后续fgets读空 while(getchar() != '\n'); if ((fp = fopen(nomeArq, "r")) == NULL) { printf("Error opening file: %s\n", nomeArq); return 1; } // 读取并跳过第一行表头 if (fgets(buffer, sizeof(buffer), fp) == NULL) { printf("File is empty\n"); fclose(fp); return 1; } // 逐行读取学生数据 while (i < 9 && fgets(buffer, sizeof(buffer), fp) != NULL) { int campoCount = 0; char *token = strtok(buffer, ";"); while (token != NULL) { // 清理token末尾的换行符 char *newlinePos = strchr(token, '\n'); if (newlinePos != NULL) { *newlinePos = '\0'; } switch(campoCount) { case 0: // 字符串拷贝留结束符位置,避免溢出 strncpy(aluno[i].nome, token, sizeof(aluno[i].nome) - 1); aluno[i].nome[sizeof(aluno[i].nome) - 1] = '\0'; break; case 1: // 字符串转浮点数存储 aluno[i].p1 = atof(token); break; case 2: aluno[i].p2 = atof(token); break; } token = strtok(NULL, ";"); campoCount++; } i++; } fclose(fp); // 传入实际读取到的学生数量 printAlunos(aluno, i); return 0; } void printAlunos(dict aluno[], int count) { for (int i = 0; i < count; i++) { printf("%s\n", aluno[i].nome); printf("%.1f %.1f\n", aluno[i].p1, aluno[i].p2); } }
关键实现说明
- 表头跳过逻辑:文件打开后优先读取第一行内容直接丢弃,不进入结构体存储流程
- 逐行重置计数器:每读取新的一行数据时,将字段计数器重置为0,保证每行的三个字段都能对应到结构体的正确成员
- 安全拷贝:姓名字段使用
strncpy拷贝,强制在末尾加字符串结束符,避免缓冲区溢出 - 换行清理:每个分割出的字段都会检查并移除末尾的换行符,避免输出时出现多余空行
- 动态计数:不硬编码学生数量,实际读取到多少条就输出多少条,兼容不同行数的CSV文件
- 类型匹配:分数字段改为
float类型,通过atof将分割得到的字符串转为数值存储,输出时用浮点数格式化符打印
内容的提问来源于stack exchange,提问作者Gabriel Carvalho
相关产品推荐
相关产品推荐

