Gabz77
Gabz77

Reputation: 11

fscanf from text file to struct pointer

I'm trying to read from a text file to a struct that has a pointer to another struct.

The text file has the following format:

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

This is my .h file:

#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 */

And my read_file func is as follows:

#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;
}

It gives me a bad_access error when trying to run the fscanf after the if is successful (when accessing the pointer of the struct for my date). If anyone could help me out, would really appreciate it.

Upvotes: 0

Views: 624

Answers (3)

chux
chux

Reputation: 154592

It gives me a bad_access error when trying to run the fscanf after the if is successful

2 problems:

  1. As @Rishikesh Raje, @Cyclonecode pointed out, the allocation is only good for aux = aux[0] as well as other missing allocations.

  2. The usual suspect to scanning problems are that the fscanf() did not scan what was expected and code lacked checks of the return value. (Hint: when line is only "0\n", the 2nd fscanf() reads more than OP expected.)

    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`
    }
    

A simply solution to both problems to (re)allocate as needed and check success of reading 2 lines and scanning them.

I recommend to not allocate data for the new pair of lines until the input have been validated.

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

Note Error checking on malloc()/realloc() omitted for brevity on the above sample code.

Upvotes: 1

Rishikesh Raje
Rishikesh Raje

Reputation: 8614

You are using both getc and fscanf to access the file.

If you want to use fscanf, you should not use getc.

Use the return value of fscanf to let you know when the file has ended. fscanf will return the number of items matched. In your case if successful it should return 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);

Upvotes: 0

Cyclonecode
Cyclonecode

Reputation: 30131

Right now you allocating memory for aux in the loop and then try to access an element using an index which will not work. Instead you need to allocate memory for all Cliente records. If you know the number of records in the file you could simply do aux = (Cliente*)malloc(size * sizeof(Cliente));. You might also check on how you can use realloc() in the actual loop.

Upvotes: 1

Related Questions