fgets()迭代数次后失效,C语言歌曲文件读写程序异常排查
C语言歌曲信息读取与输出异常排查
需求概述
从输入文件读取歌曲信息(标题、作者、时长),将时长转换为秒数并统计每首歌的重复次数,最终将处理后的数据写入输出文件。
头文件定义(headers.h)
#ifndef HEADERS #define HEADERS #define FILE_ERR -10 #define OK 0 #define TRUE 0 #define FALSE -1 #define SONGS 4 struct song { char title[31]; char author[31]; int duration_in_sec; int reps; }; int load_music_from_file(char*, struct song*); int write_music_on_file(char*, struct song*, int); // 更新函数声明 #endif
输入文件内容(songs.txt)
we will rock you queen 2:01 it's my life bon jovi 3:46 we will rock you queen 2:01 the show must go on queen 4:36
原实现代码
加载与写入函数
#include "headers.h" #include <stdio.h> #include <stdlib.h> #include <string.h> int check_reps(struct song* list, char* title, int n) { for (int i=0; i<n; i++) { if (strcmp(list[i].title, title) == 0) { list[i].reps++; return TRUE; } } return FALSE; } int min_to_sec(int min, int sec) { return (min * 60) + sec; } int load_music_from_file(char* filename, struct song* list) { FILE* fp = fopen(filename, "r"); if (fp == NULL) return FILE_ERR; int i = 0; int min, sec; char buf_title[31], buf_author[31], buf[6]; while (fgets(buf_title, 31, fp) != NULL && fgets(buf_author, 31, fp) != NULL) { if (check_reps(list, buf_title, i) == FALSE) { strncpy(list[i].title, buf_title, 31); strncpy(list[i].author, buf_author, 31); if (fgets(buf, 6, fp) != NULL && sscanf(buf, "%d:%d", &min, &sec) == 2) list[i].duration_in_sec = min_to_sec(min, sec); else return FILE_ERR; list[i].reps = 1; printf("%s%s%d\n%d\n\n", list[i].title, list[i].author, list[i].duration_in_sec, list[i].reps); } i++; } fclose(fp); return OK; } int write_music_on_file(char* filename, struct song* list) { FILE* fp = fopen(filename, "w"); if (fp == NULL) return FILE_ERR; for (int i=0; i<SONGS; i++) { fprintf(fp, "%s%s%d\n%d\n\n", list[i].title, list[i].author, list[i].duration_in_sec, list[i].reps); } return OK; }
main函数
#include "headers.h" #include <stdio.h> #include <stdlib.h> int main() { struct song* list = (struct song*) malloc(sizeof(struct song) * 4); char* input = "songs.txt"; printf("loading songs from %s\n", input); load_music_from_file(input, list); char* output = "output.txt"; printf("songs written to %s\n\n", output); write_music_on_file(output, list); free(list); return 0; }
问题现象
运行程序后,输出文件output.txt内容混乱:
we will rock you queen 121 2 it's my life bon jovi 226 1 0 0 2:01 the show must go on 0 0
问题根源与解决方案
核心问题分析
- 读取错位:原代码中,检测到歌曲重复时未读取对应时长行,导致下一次循环将时长行当作歌曲标题读取,后续所有数据读取顺序完全错位,结构体数组存入错误内容。
- 换行符干扰:
fgets会将换行符\n读入缓冲区,可能导致strcmp匹配异常(如同一标题因换行符存在与否无法匹配)。 - 固定循环次数:写入函数固定循环
SONGS次,会将未初始化的结构体元素写入文件,出现无效0和乱码。
修正后的代码
修正load_music_from_file函数
int load_music_from_file(char* filename, struct song* list) { FILE* fp = fopen(filename, "r"); if (fp == NULL) return FILE_ERR; int count = 0; // 记录实际有效歌曲数量 int min, sec; char buf_title[31], buf_author[31], buf[6]; // 每次循环强制读取完整三行信息,避免错位 while (fgets(buf_title, 31, fp) != NULL && fgets(buf_author, 31, fp) != NULL && fgets(buf, 6, fp) != NULL) { // 去掉换行符,确保字符串比较准确 buf_title[strcspn(buf_title, "\n")] = '\0'; buf_author[strcspn(buf_author, "\n")] = '\0'; if (check_reps(list, buf_title, count) == FALSE) { // 仅当歌曲未存在时,新增到结构体数组 strncpy(list[count].title, buf_title, 31); strncpy(list[count].author, buf_author, 31); if (sscanf(buf, "%d:%d", &min, &sec) == 2) { list[count].duration_in_sec = min_to_sec(min, sec); } else { fclose(fp); return FILE_ERR; } list[count].reps = 1; printf("%s\n%s\n%d\n%d\n\n", list[count].title, list[count].author, list[count].duration_in_sec, list[count].reps); count++; // 防止数组越界 if (count >= SONGS) break; } // 重复歌曲仅在check_reps中递增次数,无需其他操作 } fclose(fp); return count; // 返回有效歌曲数量,供写入函数使用 }
修正write_music_on_file函数
int write_music_on_file(char* filename, struct song* list, int count) { FILE* fp = fopen(filename, "w"); if (fp == NULL) return FILE_ERR; // 仅循环实际有效歌曲数量 for (int i=0; i<count; i++) { fprintf(fp, "%s\n%s\n%d\n%d\n\n", list[i].title, list[i].author, list[i].duration_in_sec, list[i].reps); } fclose(fp); return OK; }
修正main函数
int main() { struct song* list = (struct song*) malloc(sizeof(struct song) * SONGS); if (list == NULL) { printf("内存分配失败\n"); return EXIT_FAILURE; } char* input = "songs.txt"; printf("loading songs from %s\n", input); int song_count = load_music_from_file(input, list); if (song_count == FILE_ERR) { printf("加载歌曲失败\n"); free(list); return EXIT_FAILURE; } char* output = "output.txt"; printf("songs written to %s\n\n", output); if (write_music_on_file(output, list, song_count) == FILE_ERR) { printf("写入歌曲失败\n"); free(list); return EXIT_FAILURE; } free(list); return EXIT_SUCCESS; }
修正后效果
输出文件output.txt内容恢复正常:
we will rock you queen 121 2 it's my life bon jovi 226 1 the show must go on queen 276 1
内容的提问来源于stack exchange,提问作者sansarc
相关产品推荐
相关产品推荐

