C语言读取CSV文件到结构体时格式错误问题求助
问题:C语言读取CSV文件存入结构体失败
问题描述
在使用C语言读取CSV文件并存储到结构体时遇到以下问题:
- CSV文件每行格式为「姓名(空格分隔名+姓)+3个成绩」,但程序始终输出
File format incorrect.,调整判断值(2、3、5)也无法解决。 - 将判断条件改为
read == 1时,程序输出8条混乱的记录。
测试用CSV文件内容
Iskandar Kholmatov,100,100,100 George Washington,90,50,100 Dennis Ritchie,90,0,10 Bill Gates,60,50,77
原错误代码
#include <stdio.h> // struct to hold the name of a student struct name { char first[20]; // string to hold the first name char last[20]; // string to hold the last name }; // struct to hold the grades of a student struct student { struct name Name; // name struct from above float grades[3]; // array to hold 3 grades float average; // float to hold the average of 3 grades above }; int main(void) { // file pointer variable for accessing the file FILE *file; // attempt to open file.txt in read mode to read the file contents file = fopen("data.csv", "r"); // if the file failed to open, exit with an error message and status if (file == NULL) { printf("Error opening file.\n"); return 1; } struct student students[5]; int read = 0; // records will keep track of the number of Student records read from the file int records = 0; // read all records from the file and store them into the students array do { read = fscanf(file, "%s,%s,%f,%f,%f\n", students[records].Name.first, students[records].Name.last, &students[records].grades[0], &students[records].grades[1], &students[records].grades[2]); // if fscanf read 4 values from the file then we've successfully read // in another record if (read == 4) records++; // The only time that fscanf should NOT read 4 values from the file is // when we've reached the end of the file, so if fscanf did not read in // exactly 4 values and we're not at the end of the file, there has been // an error (likely due to an incorrect file format) and so we exit with // an error message and status. if (read != 4 && !feof(file)) { printf("File format incorrect.\n"); return 1; } // if there was an error reading from the file exit with an error message // and status if (ferror(file)) { printf("Error reading file.\n"); return 1; } } while (!feof(file)); // close the file as we are done working with it fclose(file); // print out the number of records read printf("\n%d records read.\n\n", records); // print out each of the records that was read for (int i = 0; i < records; i++) printf("%s %s %f %f %f\n", students[i].Name.first, students[i].Name.last, students[i].grades[0], students[i].grades[1], students[i].grades[2]); printf("\n"); return 0; }
问题分析与修复步骤
1. 核心错误:格式字符串匹配逻辑错误
原代码的fscanf格式串"%s,%s,%f,%f,%f\n"存在两个致命问题:
%s以空白符(空格、换行)作为结束标志,无法正确读取CSV中包含空格的姓名字段。例如第一行Iskandar Kholmatov,100,100,100,第一个%s读取Iskandar,第二个%s会跳过空格读取Kholmatov,100,100,100(逗号不是空白符),导致后续%f无法匹配有效浮点数,fscanf返回值远小于预期。- 格式串包含5个占位符(名、姓、3个成绩),成功读取时
fscanf应返回5,但原代码判断read ==4,逻辑完全错误。
2. 修正格式字符串
使用%[^,]格式说明符(匹配所有非逗号字符),结合空格匹配姓名分隔符,正确读取字段:
read = fscanf(file, "%[^ ] %[^,],%f,%f,%f\n", students[records].Name.first, students[records].Name.last, &students[records].grades[0], &students[records].grades[1], &students[records].grades[2]);
%[^ ]:匹配所有非空格字符,读取名字(如Iskandar)- 空格:匹配姓名中的空格分隔符
%[^,]:匹配所有非逗号字符,读取姓氏(如Kholmatov),:匹配CSV中的逗号分隔符
3. 修正返回值判断逻辑
成功读取一条记录时fscanf返回5,因此调整判断条件:
if (read == 5) records++; if (read != 5 && read != EOF) { printf("File format incorrect.\n"); return 1; }
4. 优化读取循环(避免多读)
do-while(!feof(file))会导致文件末尾多读一次,改为先读取再判断的结构:
while (1) { read = fscanf(file, "%[^ ] %[^,],%f,%f,%f\n", students[records].Name.first, students[records].Name.last, &students[records].grades[0], &students[records].grades[1], &students[records].grades[2]); if (read == EOF) break; // 读取到文件末尾,退出循环 if (read != 5) { printf("File format incorrect.\n"); return 1; } records++; if (records >= 5) break; // 防止数组越界 }
修复后的完整代码
#include <stdio.h> struct name { char first[20]; char last[20]; }; struct student { struct name Name; float grades[3]; float average; }; int main(void) { FILE *file = fopen("data.csv", "r"); if (file == NULL) { printf("Error opening file.\n"); return 1; } struct student students[5]; int read = 0; int records = 0; while (1) { read = fscanf(file, "%[^ ] %[^,],%f,%f,%f\n", students[records].Name.first, students[records].Name.last, &students[records].grades[0], &students[records].grades[1], &students[records].grades[2]); if (read == EOF) break; if (read == 5) records++; else { printf("File format incorrect.\n"); return 1; } if (ferror(file)) { printf("Error reading file.\n"); return 1; } if (records >= 5) break; } fclose(file); printf("\n%d records read.\n\n", records); for (int i = 0; i < records; i++) printf("%s %s %.2f %.2f %.2f\n", students[i].Name.first, students[i].Name.last, students[i].grades[0], students[i].grades[1], students[i].grades[2]); printf("\n"); return 0; }
预期输出
4 records read. Iskandar Kholmatov 100.00 100.00 100.00 George Washington 90.00 50.00 100.00 Dennis Ritchie 90.00 0.00 10.00 Bill Gates 60.00 50.00 77.00
内容的提问来源于stack exchange,提问作者Iskandar
相关产品推荐
相关产品推荐

