使用strtok()时未正确检测EOF导致重复读取数据问题
问题:读取书籍CSV数据时重复输出最后一条记录
数据格式
9780136019701,An Introduction to Organic Chemistry,Timberlake Karen,10,3.54,12-2008 9781506304212,Mathematics for Social Scientists,Kropko Jonathan,7,4.73,12-2015 9781506304213,Discrete Mathematics,Jonathan,15,19.73,10-2013 9780136019702,Chemotherapy,Karen,1,2.54,1-2002 9781152304222,Advanced Mathematics Techniques,Antony Smith,5,5.21,2-2007 9781506304215,Social Studies,Simon Minter,10,9.73,11-2013
原错误代码
int load(Book *books, int* numberOfBooks){ system("cls"); // Implement int bookCounter = 0; char c; char line[STRING_LENGTH]; FILE *file = fopen("books.txt", "r"); if (file == NULL){ printf("Error opening file"); exit(1); } while (!feof(file)) { fgets(line, STRING_LENGTH, file); line[strcspn(line, "\n")] = 0; printf("\n%s \n", line); char *bookToken = strtok(line, ","); strcpy(books[bookCounter].isbn, bookToken); bookToken = strtok(NULL, ","); strcpy(books[bookCounter].title, bookToken); bookToken = strtok(NULL, ","); strcpy(books[bookCounter].author, bookToken); bookToken = strtok(NULL, ","); int quantity = atoi(bookToken); books[bookCounter].quantity = quantity; bookToken = strtok(NULL, ","); double price = atof(bookToken); books[bookCounter].price = price; bookToken = strtok(NULL, ","); strcpy(books[bookCounter].date, bookToken); } bookCounter++; } fclose(file); printf("%d", bookCounter); return 0; }
错误现象
程序运行时会重复输出最后一条记录的ISBN,输出如下:
780136019701,An Introduction to Organic Chemistry,Timberlake Karen,10,3.54,12-2008 781506304212,Mathematics for Social Scientists,Kropko Jonathan,7,4.73,12-2015 781506304213,Discrete Mathematics,Jonathan,15,19.73,10-2013 780136019702,Chemotherapy,Karen,1,2.54,1-2002 781152304222,Advanced Mathematics Techniques,Antony Smith,5,5.21,2-2007 781506304215,Social Studies,Simon Minter,10,9.73,11-2013 781506304215
错误原因
!feof(file)的误用:这是C语言读取文件的典型错误。feof()只有在程序尝试读取超过文件末尾的内容后才会返回非零值。读取到最后一行有效数据后,!feof(file)仍为真,循环会再执行一次:此时fgets()读取失败,line数组保留着上一次的内容,导致重复处理。- 语法与计数逻辑错误:原代码中
bookCounter++;被放在循环外部的多余大括号内,导致整个循环结束后才计数一次,完全无法实现逐行计数。 - 无空指针检查:未判断
strtok()返回的指针是否为NULL,若遇到格式错误的行(字段不足6个),会触发空指针访问崩溃。
修复方案
- 替换循环条件:用
fgets()的返回值作为循环判断依据,直接确认是否成功读取一行数据。 - 修正计数位置:将
bookCounter++;移到循环内部,每处理完一行就递增计数。 - 添加空指针检查:确保每个
strtok()返回的指针不为NULL,避免非法内存访问。 - 修正语法错误:移除多余的大括号,保证代码结构正确。
修复后的代码
#include <stdio.h> #include <stdlib.h> #include <string.h> // 假设Book结构体定义如下 typedef struct { char isbn[20]; char title[100]; char author[50]; int quantity; double price; char date[10]; } Book; #define STRING_LENGTH 256 int load(Book *books, int* numberOfBooks){ system("cls"); int bookCounter = 0; char line[STRING_LENGTH]; FILE *file = fopen("books.txt", "r"); if (file == NULL){ printf("Error opening file"); exit(1); } // 用fgets的返回值判断读取是否成功 while (fgets(line, STRING_LENGTH, file) != NULL) { // 去掉换行符 line[strcspn(line, "\n")] = 0; // 跳过空行 if (strlen(line) == 0) { continue; } printf("\n%s \n", line); char *bookToken = strtok(line, ","); if (bookToken == NULL) continue; strcpy(books[bookCounter].isbn, bookToken); bookToken = strtok(NULL, ","); if (bookToken == NULL) continue; strcpy(books[bookCounter].title, bookToken); bookToken = strtok(NULL, ","); if (bookToken == NULL) continue; strcpy(books[bookCounter].author, bookToken); bookToken = strtok(NULL, ","); if (bookToken == NULL) continue; books[bookCounter].quantity = atoi(bookToken); bookToken = strtok(NULL, ","); if (bookToken == NULL) continue; books[bookCounter].price = atof(bookToken); bookToken = strtok(NULL, ","); if (bookToken == NULL) continue; strcpy(books[bookCounter].date, bookToken); bookCounter++; } fclose(file); *numberOfBooks = bookCounter; // 更新输出参数 printf("Total books loaded: %d\n", bookCounter); return 0; }
内容的提问来源于stack exchange,提问作者Saif eldeen Adel
相关产品推荐
相关产品推荐

