C语言读取特定格式TXT文件并写入队列时异常问题排查
问题排查:从TXT文件加载队列数据失败
问题背景
目标TXT文件格式
11/01/2010: did not pass the test! :( 5 10/02/2010: not even this time I passed it! >:( 2 16/04/2010: is the third time I retake this exam!!! >:@ 1 20/05/2024: finally passed it! :D 14
队列数据结构定义
#define N_QUEUE 4 #define OK 0 #define FILE_ERR 1 #define EMPTY 2 #define FULL 3 #define FALSE 1 struct post { char msg[257]; int n_like; }; struct feed { struct post* storage; int busy; }; void init(struct feed* queue) { queue->storage = malloc(sizeof(struct post) * N_QUEUE); queue->busy = 0; }
现有实现代码
队列操作函数
int append(struct feed* queue, struct post s) { if (queue->busy >= N_QUEUE) return handle_err(FULL); queue->storage[queue->busy] = s; queue->busy++; return OK; } struct post pop(struct feed* queue) { if (queue->busy <= 0) handle_err(EMPTY); struct post s = queue->storage[0]; for (int i=0; i<queue->busy-1; i++) { queue->storage[i] = queue->storage[i+1]; } queue->busy--; return s; }
文件加载函数(存在问题)
int load_user_from_file(char* filename, struct feed* queue) { FILE *fp = fopen(filename, "r"); if (fp == NULL) return handle_err(FILE_ERR); struct post s; while (fgets(s.msg, 257, fp) != NULL) { while (fscanf(fp, "%d", &s.n_like) == 1) { append(&(*queue), s); } } return OK; }
主函数
int main() { struct feed queue; init(&queue); char* filename = "user42.txt"; load_user_from_file(filename, &queue); return 0; }
错误处理函数
int handle_err(int errno) { switch (errno) { case FILE_ERR: printf("\nerror (%d): could not write/read the file\n\n", FILE_ERR); break; case EMPTY: printf("\nerror (%d): queue is empty\n\n", EMPTY); break; case FULL: printf("\nerror (%d): queue reached its full space\n\n", FULL); break; default: printf("\nunknown error occurred\n\n"); } exit(FALSE); }
错误现象
运行程序后队列中得到空字符串和0;加入handle_err后触发栈错误。
问题分析
load_user_from_file函数及关联代码存在三个核心问题:
- 内层while循环逻辑错误:每个消息行对应一个点赞数,不需要内层循环。当
fscanf读取到第一个点赞数后,会尝试继续读取下一个,但下一个内容是消息行,fscanf("%d")匹配失败,此时文件指针停在消息行起始位置,后续fgets会读取残缺内容,导致数据混乱。 - 未处理
fgets读取的换行符:fgets会把换行符\n读入msg数组,导致消息内容包含多余换行,甚至影响后续字符串处理。 handle_err参数名冲突:参数名errno是系统标准宏(定义在<errno.h>),使用它会导致命名冲突,引发未定义行为。
修复方案
修正后的load_user_from_file函数
int load_user_from_file(char* filename, struct feed* queue) { FILE *fp = fopen(filename, "r"); if (fp == NULL) return handle_err(FILE_ERR); struct post s; while (fgets(s.msg, 257, fp) != NULL) { // 去掉fgets读取的换行符 size_t len = strlen(s.msg); if (len > 0 && s.msg[len-1] == '\n') { s.msg[len-1] = '\0'; } // 读取对应的点赞数,单次读取即可 int scan_result = fscanf(fp, "%d", &s.n_like); if (scan_result != 1) { fprintf(stderr, "Failed to read like count for message: %s\n", s.msg); break; } // 跳过点赞数后面的换行符,避免下一次fgets读取空行 int c; while ((c = fgetc(fp)) != '\n' && c != EOF); // 将帖子加入队列 append(queue, s); } fclose(fp); return OK; }
修正handle_err参数名
int handle_err(int err_code) { // 将errno改为err_code,避免系统宏冲突 switch (err_code) { case FILE_ERR: printf("\nerror (%d): could not write/read the file\n\n", FILE_ERR); break; case EMPTY: printf("\nerror (%d): queue is empty\n\n", EMPTY); break; case FULL: printf("\nerror (%d): queue reached its full space\n\n", FULL); break; default: printf("\nunknown error occurred\n\n"); } exit(FALSE); }
额外优化建议
- 释放内存:在
main函数结束前,用free(queue.storage)释放init中分配的内存,避免内存泄漏。 append函数简化:handle_err会直接调用exit,因此return handle_err(FULL)可简化为handle_err(FULL),无需return语句。pop函数语法补全:虽然handle_err会退出程序,但为了语法完整,可在handle_err后添加return (struct post){0};(需C99及以上标准支持)。
验证
修复后运行程序,队列会正确加载TXT文件中的4条记录,每条记录的消息内容和点赞数与文件完全一致。
内容的提问来源于stack exchange,提问作者sansarc
相关产品推荐
相关产品推荐

