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

如何用fscanf从文本文件读取数据到含结构体指针的结构体?

使用fscanf读取含结构体指针的嵌套数据

嘿,我来帮你搞定这个读取问题!首先从你给出的文本片段和头文件开头,我先梳理下数据逻辑和结构体的大概结构,然后一步步写代码示例给你。

先明确数据和结构体的对应关系

从你提供的文本内容来看,每一组客户数据的格式大概是:

客户ID 状态码 姓名(含空格) 客户编号 租赁记录数 [N条租赁记录的字段]

而你的Cliente结构体里应该包含一个指向Alugueres(租赁记录)结构体的指针——毕竟你引入了Alugueres.h对吧?我先把这两个结构体补全(根据文本格式推断),这样后面的读取逻辑才有依据:

补全Alugueres.h的结构体定义

#ifndef Alugueres_h
#define Alugueres_h

#define ST_TAM 50 // 和Clientes.h保持一致

typedef struct aluguer Aluguer;
struct aluguer {
    int dia;
    int mes;
    int ano;
    long long aluguer_id;
    int estado;
    char nome_contato[ST_TAM];
    int flag;
};

#endif

补全Clientes.h的结构体定义

#ifndef Clientes_h
#define Clientes_h
#include <stdio.h>
#include <stdlib.h>
#include "Alugueres.h"
#define ST_TAM 50

typedef struct info_cliente Cliente;
struct info_cliente{
    long long cliente_id;
    int estado;
    char nome[ST_TAM];
    char codigo_cliente[10]; // 存储002这类短编号
    int num_alugueres; // 该客户的租赁记录数量
    Aluguer *alugueres; // 指向租赁记录数组的动态指针
};

#endif

核心读取逻辑实现

思路很清晰:先读客户的基础信息,根据租赁记录数动态分配内存,再循环读取每条租赁记录。我写个完整的示例函数,你可以直接参考:

#include "Clientes.h"

// 读取单个客户的完整数据(含租赁记录)
Cliente* ler_cliente(FILE *arquivo) {
    // 先给客户结构体分配内存
    Cliente *cliente = malloc(sizeof(Cliente));
    if (!cliente) {
        perror("Erro ao alocar memória para cliente");
        return NULL;
    }

    // 注意:姓名是带空格的(比如John Doe),%s只能读单个单词,所以分两部分读再合并
    char nome_part1[ST_TAM], nome_part2[ST_TAM];
    // 读取客户基础字段:ID、状态、名、姓、客户编号、租赁记录数
    int ret = fscanf(arquivo, "%lld %d %s %s %s %d", 
                     &cliente->cliente_id, 
                     &cliente->estado, 
                     nome_part1, nome_part2, 
                     cliente->codigo_cliente, 
                     &cliente->num_alugueres);
    
    // 检查是否读取了预期的6个字段
    if (ret != 6) {
        free(cliente);
        return NULL;
    }
    // 合并名和姓成完整姓名
    snprintf(cliente->nome, ST_TAM, "%s %s", nome_part1, nome_part2);

    // 如果有租赁记录,动态分配内存
    if (cliente->num_alugueres > 0) {
        cliente->alugueres = malloc(cliente->num_alugueres * sizeof(Aluguer));
        if (!cliente->alugueres) {
            perror("Erro ao alocar memória para alugueres");
            free(cliente);
            return NULL;
        }

        // 循环读取每条租赁记录
        for (int i = 0; i < cliente->num_alugueres; i++) {
            Aluguer *aluguer_atual = &cliente->alugueres[i];
            // 租赁记录的字段:日 月 年 租赁ID 状态 联系人姓名(带空格) 标识
            char contato_part1[ST_TAM], contato_part2[ST_TAM];
            ret = fscanf(arquivo, "%d %d %d %lld %d %s %s %d",
                         &aluguer_atual->dia, &aluguer_atual->mes, &aluguer_atual->ano,
                         &aluguer_atual->aluguer_id, &aluguer_atual->estado,
                         contato_part1, contato_part2,
                         &aluguer_atual->flag);
            
            if (ret != 8) {
                // 读取失败,释放已分配的所有内存
                free(cliente->alugueres);
                free(cliente);
                return NULL;
            }
            // 合并联系人姓名
            snprintf(aluguer_atual->nome_contato, ST_TAM, "%s %s", contato_part1, contato_part2);
        }
    } else {
        // 没有租赁记录,指针置空避免野指针
        cliente->alugueres = NULL;
    }

    return cliente;
}

// 读取文件中所有客户的示例函数
void processar_arquivo_clientes(const char *caminho) {
    FILE *arquivo = fopen(caminho, "r");
    if (!arquivo) {
        perror("Erro ao abrir o arquivo");
        return;
    }

    Cliente *cliente_atual;
    // 循环读取直到文件结束
    while ((cliente_atual = ler_cliente(arquivo)) != NULL) {
        // 这里可以替换成你自己的业务逻辑,比如打印验证
        printf("=== Cliente ===\nID: %lld\nNome: %s\nNumero de alugueres: %d\n",
               cliente_atual->cliente_id, cliente_atual->nome, cliente_atual->num_alugueres);
        
        // 记得用完后释放内存,避免泄漏
        if (cliente_atual->alugueres) {
            free(cliente_atual->alugueres);
        }
        free(cliente_atual);
    }

    fclose(arquivo);
}

// 主函数测试
int main() {
    processar_arquivo_clientes("seu_arquivo.txt");
    return 0;
}

几个关键注意事项

  1. 处理带空格的字符串:fscanf的%s只会读取到第一个空格,所以像姓名这种带空格的字段,我分成两个部分读取后用snprintf合并。如果姓名可能有更多部分(比如中间名),你可以考虑用fgets配合sscanf来处理,但这个方案对于你的文本格式已经足够。
  2. 动态内存管理:每次分配内存后都要检查是否成功,使用完结构体后一定要释放内存(包括租赁记录的数组),不然会造成内存泄漏。
  3. 格式严格匹配:fscanf的格式字符串必须和文本的字段类型完全对应,比如%lld对应长整型的ID,%d对应整数,别写错了,不然会读取错误的数据。
  4. 错误检查:每次调用fscanf后都要检查返回值,确保读取了预期数量的字段,这样能避免因为文件格式错误导致的数据错位。

用你给出的文本片段测试的话,第一组客户数据会被正确解析:

  • 客户ID:279288151,姓名:John Doe,租赁记录数:1
  • 对应的租赁记录:日期30/04/2018,租赁ID:23189842,联系人:Jane Doe

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 09:51:11