You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

问题根源与解决方案

核心问题分析

  1. 读取错位:原代码中,检测到歌曲重复时未读取对应时长行,导致下一次循环将时长行当作歌曲标题读取,后续所有数据读取顺序完全错位,结构体数组存入错误内容。
  2. 换行符干扰:fgets会将换行符\n读入缓冲区,可能导致strcmp匹配异常(如同一标题因换行符存在与否无法匹配)。
  3. 固定循环次数:写入函数固定循环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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 16:17:01