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

C语言读取歌曲文件至elenco链表时出现乱码问题求助

问题原因分析

乱码出现主要源于三个核心问题:

  1. 错误使用feof()作为循环条件:feof()仅在尝试读取文件末尾后才会返回真,这会导致循环多执行一次,最后一次分配的结构体无有效数据,输出时读取到内存垃圾值。
  2. fscanf("%d:%d")遗留换行符:读取完时长的minuti和secondi后,该行末尾的换行符会留在输入缓冲区,下一次调用fscanf("%29[^\n]")会读取空字符串,且目标数组未被正确初始化(缺少字符串终止符\0),printf输出时越界读取内存导致乱码。
  3. 未处理fscanf读取失败场景:若某次fscanf读取失败,结构体的字符数组会保留malloc后的垃圾值,输出时同样触发乱码。
修复方案

以下是修正后的完整代码,改用更可靠的fgets读取每行内容,彻底解决缓冲区和初始化问题:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct elenco{
    char titolo[30];
    char autore[30];
    int durata_in_sec;
    int rips;
    struct elenco *prossima;
};

// 辅助函数:去除字符串末尾的换行符
void trim_newline(char *str) {
    size_t len = strlen(str);
    if (len > 0 && str[len-1] == '\n') {
        str[len-1] = '\0';
    }
}

int main()
{
    FILE *fp = fopen("musica.txt", "r");
    if (!fp) { // 检查文件是否成功打开
        perror("无法打开文件");
        return 1;
    }

    int i = 0;
    int minuti, secondi;
    char durata_str[10];
    struct elenco *start = NULL;
    struct elenco *elenco = NULL;
    struct elenco *prec = NULL;

    // 循环读取:每次读取标题、作者、时长三行
    while (1) {
        elenco = malloc(sizeof(struct elenco));
        if (!elenco) { // 检查内存分配是否成功
            perror("内存分配失败");
            return 1;
        }

        // 读取标题
        if (!fgets(elenco->titolo, sizeof(elenco->titolo), fp)) {
            free(elenco); // 读取失败,释放刚分配的内存
            break;
        }
        trim_newline(elenco->titolo);

        // 读取作者
        if (!fgets(elenco->autore, sizeof(elenco->autore), fp)) {
            free(elenco);
            break;
        }
        trim_newline(elenco->autore);

        // 读取时长字符串并解析
        if (!fgets(durata_str, sizeof(durata_str), fp)) {
            free(elenco);
            break;
        }
        trim_newline(durata_str);
        if (sscanf(durata_str, "%d:%d", &minuti, &secondi) != 2) {
            free(elenco);
            break;
        }

        // 填充结构体字段
        elenco->durata_in_sec = minuti * 60 + secondi;
        elenco->rips = 1;
        elenco->prossima = NULL;

        // 更新链表
        if (i == 0) {
            start = elenco;
        } else {
            prec->prossima = elenco;
        }
        prec = elenco;
        i++;
    }
    
    // 输出链表内容
    elenco = start;
    while (elenco != NULL) {
        printf("%s\n", elenco->titolo);
        printf("%s\n", elenco->autore);
        printf("%d\n", elenco->durata_in_sec);
        printf("%d\n", elenco->rips);
        elenco = elenco->prossima;
    }

    fclose(fp);

    // 释放链表内存
    elenco = start;
    while (elenco != NULL) {
        struct elenco* temp = elenco;
        elenco = elenco->prossima;
        free(temp);
    }

    return 0;
}
关键修复点说明
  • 替换循环逻辑:不再依赖feof(),通过fgets的返回值判断读取状态,避免循环多执行一次导致的垃圾值输出。
  • 改用fgets读取每行:fgets会读取整行内容(含换行符),配合trim_newline函数去除末尾换行,确保字符数组始终以\0结尾,杜绝printf越界读取。
  • 添加全面错误检查:对文件打开、内存分配、内容读取、时长解析的所有步骤都做了错误检查,避免意外错误引发内存泄漏或乱码。
  • 分离时长读取与解析:先读取时长字符串再用sscanf解析,彻底解决fscanf遗留的缓冲区换行符问题。

内容的提问来源于stack exchange,提问作者ShishRobot

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 06:02:07