【问题标题】:fscanf from text file to struct pointerfscanf 从文本文件到结构指针
【发布时间】:2018-04-30 11:44:37
【问题描述】:

我正在尝试从一个文本文件读取一个结构,该结构具有指向另一个结构的指针。

文本文件格式如下:

279288151 1 John Doe
002 1 30 04 2018
23189842 0 Jane Doe
0
282676381 1 Mark Examp
001 0 28 03 2018 03 04 2018
243897574 1 Joe Soap
003 2 14 04 2018 21 04 2018

这是我的 .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{
    char nome[ST_TAM];
    long nif;
    int n_alugueres;
    int n_hist;
    pAluga aluguer;
};

typedef struct aluga Aluguer, *pAluga;
typedef struct data DataIn, *pDataIn;
typedef struct data DaraEn, *pDataEn;

struct aluga{
    int id_unico;
    int estado;
    pDataIn dataIn;
    pDataEn dataEn;
    pAluga prox;
};

struct data{
    int dia;
    int mes;
    int ano;
};

Cliente* le_fich(char *nome, int *n);

#endif /* Clientes_h */

而我的 read_file 函数如下:

#include "Clientes.h"

Cliente* le_fich(char *nome, int *n){

    FILE *f = fopen(nome, "r");
    Cliente *aux;
    int conta = 0;

    if(!f){
        printf("Error\n");
        return NULL;
    }

    while(getc(f) != EOF){
        aux = (Cliente*)malloc(sizeof(Cliente));
        fscanf(f, "%ld %d %49[^\n]", &aux[conta].nif, &aux[conta].n_alugueres, aux[conta].nome);
        if(aux[conta].n_alugueres != 0){
            fscanf(f, "%d %d %d %d %d", &aux[conta].aluguer->id_unico, 
            &aux[conta].aluguer->estado, &aux[conta].aluguer->dataIn->dia, 
            &aux[conta].aluguer->dataIn->mes, &aux[conta].aluguer->dataIn->ano);
        }
        conta++;
    }
return aux;
}

在 if 成功后尝试运行 fscanf 时出现 bad_access 错误(访问我的日期的结构指针时)。如果有人可以帮助我,将不胜感激。

【问题讨论】:

  • @Cyclonecode 我知道什么时候创建文件,但是一旦我到达文件末尾,我应该确定值。如果我在循环的开头放一个malloc,那会解决问题,然后用指针访问不同的字段吗?
  • while(getc(f) != EOF){ 丢失文件的第一个字符,'2'
  • 关于:printf("Error\n"); 1) 错误消息应该输出到stderr,而不是stdout。 2) 还应输出系统认为发生错误的文本原因。建议:perror( "fopen failed" ); 专为此目的而创建
  • 关于:aux = (Cliente*)malloc(sizeof(Cliente)); 1) 任何堆分配函数的返回类型:malloccallocreallocvoid*,可以分配给任何指针。强制转换只会使代码混乱,使其更难以理解、调试等。 2) 始终检查 (!=NULL) 返回值以确保操作成功。如果不成功,调用perror()将你的错误信息和系统认为错误发生的原因文本输出到stderr
  • 强烈建议替换:`while(getc(f) != EOF){ aux = (Cliente*)malloc(sizeof(Cliente)); fscanf(f, "%ld %d %49[^\n]", &aux[conta].nif, &aux[conta].n_alugueres, aux[conta].nome);` with: char buffer[1024]; while( fgets( buffer, sizeof buffer, f ) { call malloc() then use sscanf() to extract fieldsL &amp;aux[conta].nif, &amp;aux[conta].n_alugueres, aux[conta].nome)

标签: c pointers struct


【解决方案1】:

现在您在循环中为aux 分配内存,然后尝试使用不起作用的索引访问元素。相反,您需要为所有 Cliente 记录分配内存。如果您知道文件中的记录数,您可以简单地执行aux = (Cliente*)malloc(size * sizeof(Cliente));。您还可以检查如何在实际循环中使用realloc()

【讨论】:

    【解决方案2】:

    if 成功后尝试运行fscanf 时出现bad_access 错误

    2 个问题:

    1. 正如@Rishikesh Raje@Cyclonecode 所指出的,分配只适用于aux = aux[0] 以及其他缺失的分配。

    2. 扫描问题的常见问题是fscanf() 没有扫描预期的内容,并且代码缺少对返回值的检查。 (提示:当只有"0\n" 行时,第二个fscanf() 读取的内容比OP 预期的要多。)

      ret = fscanf(f, "%ld %d %49[^\n]", ...);
      if((ret ==3) && (aux[conta].n_alugueres != 0)){
          fscanf(f, "%d %d %d %d %d", ...
          // code errantly does not check the return value of `fscanf()`.
      }
      else {
        break; // This code missing, what to do when `ret != 3`
      }
      

    根据需要(重新)分配并检查读取 2 行并扫描它们是否成功,这两个问题的简单解决方案。

    我建议在验证输入之前不要为新的行对分配数据。

    Cliente *aux = NULL; // Initialize to NULL
    size_t n = 0;    // Keep count of record count
    char buf1[150];  // Use generous buffers.  Suggest 2x maximum expected need
    char buf2[100];
    
    // while 2 lines successfully read
    while (fgets(buf1, sizeof buf1, f) && fgets(buf2, sizeof buf2, f)) {
      // Form objects to be scanned into with default values.
      struct info_cliente cli = { 0 };
      struct aluga alu = { 0 };
      struct data dat = { 0 };
    
      if (sscanf(buf1, "%ld %d %49[^\n]", &cli.nif, &cli.n_alugueres, cli.nome) != 3) {
        perror("Unexpected 1st line");
        break;
      }
      if (cli.n_alugueres == 0) {
        if (sscanf(buf2, "%d", &alu.id_unico) != 1 || alu.id_unico != 0)) {
          perror("Unexpected 2nd line 0");
          break;
        }
      }
      else if (cli.n_alugueres == 1) {
        if (sscanf(buf2, "%d %d %d %d %d", &alu.id_unico, &alu.estado, &dat.dia,
            &dat.mes, &dat.ano) != 5) {
          perror("Unexpected 2nd line");
          break;
        }
        alu.dataIn = malloc(sizeof *alu.dataIn);
        *alu.dataIn = dat;
        cli.aluguer = malloc(sizeof *cli.aluguer);
        *cli.aluguer = alu;
      } else {
        perror("Unexpected 2nd line n_alugueres");
        break;
      }
      Cliente *tmp = realloc(aux, sizeof *aux * (n+1));
      aux = tmp;
      aux[n] = cli;
      n++;
    }
    
    Cliente *tmp = realloc(aux, sizeof *aux * (n+1));
    aux = tmp;
    aux[n] = NULL;  // NULL terminate the list
    

    注意为简洁起见,上述示例代码省略了对malloc()/realloc() 的错误检查。

    【讨论】:

      【解决方案3】:

      同时使用 getcfscanf 来访问该文件。

      如果你想使用fscanf,你不应该使用getc

      使用fscanf 的返回值让您知道文件何时结束。 fscanf 将返回匹配的项目数。在您的情况下,如果成功,它应该返回3

      int ret;
      do{
          aux = (Cliente*)malloc(sizeof(Cliente));
          ret = fscanf(f, "%ld %d %49[^\n]", &aux[conta].nif, &aux[conta].n_alugueres, aux[conta].nome);
          if((ret ==3) && (aux[conta].n_alugueres != 0)){
              fscanf(f, "%d %d %d %d %d", &aux[conta].aluguer->id_unico, 
              &aux[conta].aluguer->estado, &aux[conta].aluguer->dataIn->dia, 
              &aux[conta].aluguer->dataIn->mes, &aux[conta].aluguer->dataIn->ano);
          }
          conta++;
      }while (ret == 3);
      

      【讨论】:

      • 代替aux = (Cliente*)malloc(sizeof(Cliente));,考虑更简单的aux = malloc(sizeof *aux);
      • aux[conta] 在第一次迭代后将是无效内存。它需要类似于aux = realloc(sizeof(aux[0]) * (conta + 1)); 的东西(需要在循环之前将aux 初始化为NULL)。
      • 关于:fscanf(f, "%d %d %d %d %d", &amp;aux[conta].aluguer-&gt;id_unico, &amp;aux[conta].aluguer-&gt;estado, &amp;aux[conta].aluguer-&gt;dataIn-&gt;dia, &amp;aux[conta].aluguer-&gt;dataIn-&gt;mes, &amp;aux[conta].aluguer-&gt;dataIn-&gt;ano); 应始终检查返回值。所以这个对fscanf() 的调用应该包含在if() 语句中,并且返回值与4 进行比较
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-09-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-12-25
      • 2022-11-30
      相关资源
      最近更新 更多