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

C语言读取文本文件至结构体数组异常问题咨询

解决文件读取导致的结构体数据错位问题

看起来你踩了两个常见的C语言文件读取坑:一是完全误解了文件的格式结构,二是混用fscanf和fgets时没处理缓冲区残留的换行符,导致数据完全错位。咱们一步步解决这个问题:

问题根源分析

  1. 格式理解错误:你的文本文件里每个用户的所有字段都在同一行(比如112 pass222 Ginge Akerwood Camella Homes 0922294812是一个用户的完整数据),但你的getData函数却错误地认为每个字段单独占一行,用fgets分别读取name、address、contactNum,直接导致读取的内容完全错位。
  2. 缓冲区残留问题:fscanf读取完ID和password后,会把行尾的换行符留在输入缓冲区里,第一个fgets会直接读取这个空换行符,后续的fgets会读取下一行的错误内容,最终结构体数组的数据彻底混乱,出现你看到的ID被contactNum覆盖的异常输出。

完整修正方案

我们改用整行读取后拆分字段的方式,既避免缓冲区问题,又能正确处理带空格的name和address字段:

第一步:确保头文件定义正确

先确认Data.h里的字符串宏和结构体定义是正确的:

#define string10 char[11]
#define string20 char[21]
#define string30 char[31]
#define string15 char[16]

typedef struct userTag {
    int ID;
    string10 password;
    string20 name;
    string30 address;
    string15 contactNum;
} userType;

第二步:修正getData和主程序代码

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

void display(userType u[], int nElem) {
    int i;
    for(i = 0; i < nElem; i++) {
        printf("=== User %d ===\n", i+1);
        printf("User ID: %d\n", u[i].ID);
        printf("Password: %s\n", u[i].password);
        printf("Name: %s\n", u[i].name);
        printf("Address: %s\n", u[i].address);
        printf("Contact Num: %s\n\n", u[i].contactNum);
    }
}

void getData(userType u[]) {
    int i = 0;
    FILE* fp_users = fopen("Users.txt", "r");
    if (!fp_users) {
        perror("Failed to open Users.txt");
        return;
    }

    char line[150]; // 足够容纳一行的所有内容
    while (fgets(line, sizeof(line), fp_users) != NULL && i < 20) {
        // 移除行尾的换行符/回车符
        size_t len = strlen(line);
        if (len > 0 && (line[len-1] == '\n' || line[len-1] == '\r')) {
            line[len-1] = '\0';
        }

        // 提取最后一个字段contactNum(无空格,最可靠的拆分点)
        char* contact_pos = strrchr(line, ' ');
        if (!contact_pos) continue; // 格式错误,跳过该行
        strncpy(u[i].contactNum, contact_pos + 1, sizeof(u[i].contactNum) - 1);
        u[i].contactNum[sizeof(u[i].contactNum) - 1] = '\0';
        *contact_pos = '\0'; // 截断字符串,去掉contactNum部分

        // 提取ID和password(无空格字段,用sscanf直接读取)
        int temp_id;
        char temp_pass[11];
        if (sscanf(line, "%d %s", &temp_id, temp_pass) != 2) continue;
        u[i].ID = temp_id;
        strncpy(u[i].password, temp_pass, sizeof(u[i].password) - 1);
        u[i].password[sizeof(u[i].password) - 1] = '\0';

        // 提取name和address(带空格的字段)
        char* name_addr_start = line;
        // 跳过ID和password的内容,定位到name的起始位置
        while (*name_addr_start != ' ') name_addr_start++;
        while (*name_addr_start == ' ') name_addr_start++;
        while (*name_addr_start != ' ') name_addr_start++;
        while (*name_addr_start == ' ') name_addr_start++;

        // 按结构体长度分配name和address:name最多20字符,剩下的给address
        strncpy(u[i].name, name_addr_start, sizeof(u[i].name) - 1);
        u[i].name[sizeof(u[i].name) - 1] = '\0';
        // 跳过name后的空格,复制剩余内容到address
        char* addr_start = name_addr_start + strlen(u[i].name);
        while (*addr_start == ' ') addr_start++;
        strncpy(u[i].address, addr_start, sizeof(u[i].address) - 1);
        u[i].address[sizeof(u[i].address) - 1] = '\0';

        i++;
    }

    fclose(fp_users);
}

int main(int argc, char *argv[]) {
    userType user[20];
    memset(user, 0, sizeof(user)); // 初始化数组,避免垃圾值
    getData(user);
    display(user, 2); // 你的文件只有2个用户,传2而非3
    return 0;
}

关键修正点说明

  1. 整行读取:用fgets读取每一行,彻底避免fscanf和fgets混用的缓冲区残留问题。
  2. 可靠拆分contactNum:用strrchr找到最后一个空格,拆分出无空格的contactNum,这是最稳定的拆分方式。
  3. 字段边界处理:手动定位name的起始位置,按结构体定义的长度分配name和address的内容,兼容带空格的字段。
  4. 错误检查:添加文件打开失败的检查,避免空指针访问;跳过格式错误的行,增强程序鲁棒性。
  5. 数组初始化:用memset初始化结构体数组,避免未读取的用户字段出现垃圾值。

现在运行程序,就能正确输出每个用户的所有字段,ID不会再出现异常了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 19:12:47