Frank
Frank

Reputation: 3

How to copy from a file to an array

I have to read an array from a file and the fact i that's the last column is just not being copied. The size of the array and it content is in the file. Here is the code (only the part where the problem is):

#include<stdio.h>
#include<stdlib>
int main(int argc, char * argv[]){ 
char **laberinto;
char aux;
fent = fopen(argv[1], "r");
if
 fprintf(stderr,"Couldn't open.\n");
else
{
 laberinto = (char **) calloc(columnas, sizeof(char *));
 for (i = 0; (NULL != laberinto) && (i < columnas); i++)
  {
      laberinto[i] = (char *) calloc(filas, sizeof(char));
      if (NULL == laberinto[i])
       {
        for ( j = i - 1; j >= 0; j-- )
          free(laberinto[j]); 
        free(laberinto);
        laberinto = NULL;
       }
  }
 for(i = 0, j = 0; i < filas+1; i++)
    for(j = 0; j < columnas+1; j++)
      {
        if(!feof(fent))
          {
            aux = fgetc(fent);
            if(aux == '\n')
            aux = 0;
           }}

Edit 1(full code) it generates a core by the way:

#include<stdio.h>
#include<stdlib.h>
#include"encruta1.h"//Just funcion definitions(not used yet)
#define ERRORCOMANDO "ERROR: linea de comandos incorrecta. Utilice:\n" 
#define ERRORCOMAND2 "leelabfich fichero_de_entrada columna_inicio fila_inicio\n"
//Both are error messages

int main(int argc, char *argv[])
{
  char **laberinto;
  char aux;
  int i = 0;//Indice para imprimir la tabla
  int j = 0;//Indice para imprimir la tabla
  int columnas = 0;
  int filas = 0;
  int flag1;//Para saber si fscanf ha funcionado bien
  FILE *fent;

  //Si son cuatro argumentos es correcto
  if(argc == 4)
   {
    //Apertura de archivo
    fent = fopen(argv[1], "r");
    if(fent == NULL)
     fprintf(stderr,"No se puede abrir el fichero de entrada.\n");
    else
    {
     flag1 = fscanf(fent,"%d%d", &columnas, &filas);
     if(flag1 == 2)
     {
      if(filas < 0 || columnas < 0)
       fprintf(stderr,"Las dimensiones han de ser dos numeros enteros.\n");
      else
       {
       //Reserva de memoria   
       laberinto = (char **) calloc(columnas, sizeof(char *));
       for (i = 0; (NULL != laberinto) && (i < columnas); i++)
        {
          laberinto[i] = (char *) calloc(filas, sizeof(char));
          if (NULL == laberinto[i])
          {
        for ( j = i - 1; j >= 0; j-- )
          free(laberinto[j]); 
        free(laberinto);
        laberinto = NULL;
          }
        }

      //Pasamos el laberinto del archivo a la tabla

      for(i = 0, j = 0; i < filas+1; i++)
        for(j = 0; j < columnas+1; j++)
          {
        if(!feof(fent))
          {
            aux = fgetc(fent);
            if(aux == '\n')
              aux = 0;
            else
              laberinto[i][j] = aux;
          }
          }

      for(i = 0; i < filas; i++)
        {
          for(j = 0; j < columnas; j++)
        {//Eliminamos los intentos fallidos
          if(laberinto[i][j] == 'o')//Just ignore the o
            laberinto[i][j] = '.';
          printf("%c", laberinto[i][j]);
        }
          printf("\n");
        }
      //Sustituir por donde se libere memoria abajo
      for(i = 0; i < columnas+1; i++)
        free(laberinto[i]);
      laberinto = NULL;

Upvotes: 0

Views: 275

Answers (1)

David C. Rankin
David C. Rankin

Reputation: 84599

You are far better served using line-oriented input to read all lines in a file into a dynamically allocated array. The line-oriented functions available in the standard library are fgets and getline.

In this case, where you do not know what the maximum number of characters in each line will be, you are better served using getline to read each line as getline will dynamically allocate a line buffer of sufficient size for you.

If you use fgets (which is fine), you would have to add code to check for a short or incomplete read of each line and realloc and strcat the line until a complete read takes place. getline just makes life simpler here.

Your basic scheme is to declare a pointer-to-pointer-to-type (i.e. a double-pointer), allocate a reasonable number of pointers to handle the file, if you reach the initial limit, realloc 2X the number of current pointers and continue.

When done, free the allocated lines, then free the array. A quick example:

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

#define NMAX 128    /* initial number of pointers */

int main (int argc, char **argv) {

    char **array = NULL;            /* array to hold lines read         */
    char *ln = NULL;                /* NULL forces getline to allocate  */
    size_t n = 0;                   /* initial ln size, getline decides */
    ssize_t nchr = 0;               /* number of chars actually read    */
    size_t idx = 0;                 /* array index counter              */
    size_t nmax = NMAX;             /* check for reallocation           */
    size_t i = 0;                   /* general loop variable            */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin; /* open stream  */

    if (!fp)  {       /* validate stream open for reading */
        fprintf (stderr, "error: file open failed '%s',\n", argv[1]);
        return 1;
    }

    /* allocate NMAX pointers to char* */
    if (!(array = calloc (NMAX, sizeof *array))) {
        fprintf (stderr, "error: memory allocation failed.");
        return 1;
    }

    /* read each line from fp  */
    while ((nchr = getline (&ln, &n, fp)) != -1)
    {
        /* strip newline or carriage rtn    */
        while (nchr && (ln[nchr-1] == '\n' || ln[nchr-1] == '\r'))
            ln[--nchr] = 0;

        array[idx++] = strdup (ln); /* allocate/copy ln to array        */

        if (idx == nmax) {          /* if idx reaches nmax, reallocate  */
            char **tmp = realloc (array, nmax * 2 * sizeof *tmp);
            if (!tmp) {
                fprintf (stderr, "error: memory exhausted.\n");
                break;
            }
            array = tmp;    /* set new pointers NULL */
            memset (array + nmax, 0, nmax * sizeof tmp);
            nmax *= 2;
        }
    }

    if (ln) free (ln);              /* free memory allocated by getline */
    if (fp != stdin) fclose (fp);   /* close open file if not default   */

    /* print array */
    printf ("\n lines read from '%s'\n\n", argc > 1 ? argv[1] : "stdin");
    for (i = 0; i < idx; i++)
        printf ("   line[%3zu]  %s\n", i, array[i]);

    for (i = 0; i < idx; i++)
        free (array[i]);    /* free each line */
    free (array);           /* free pointers  */

    return 0;
}

Use a memory error checker (like valgrind on Linux) to confirm correct use of the memory and that all memory is properly freed when no longer needed. Look it over and let me know if you have additional questions.

Numeric Array

For numeric arrays, you approach is exactly the same. However, instead of storing the ln in an array of pointers to char, you simply parse the line as required using sscanf or preferrably strtol, etc... The changes required are minimal. e.g.:

...
#include <limits.h>
#include <errno.h>
...
long *array = NULL;             /* pointer to long                  */
int base = argc > 2 ? atoi (argv[2]) : 10; /* base (default: 10)    */
...

your read loop then looks like:

/* read each line from file - separate into array       */
while ((nchr = getline (&ln, &n, fp)) != -1)
{
    char *p = ln;      /* pointer to ln read by getline */
    char *ep = NULL;   /* endpointer for strtol         */

    while (errno == 0)
    {   /* parse/convert each number in line into array */
        array[idx++] = xstrtol (p, &ep, base);

        if (idx == nmax)        /* check NMAX / realloc */
            array = realloc_long (array, &nmax);

        /* skip delimiters/move pointer to next digit   */
        while (*ep && *ep != '-' && (*ep < '0' || *ep > '9')) ep++;
        if (*ep)
            p = ep;
        else
            break;
    }
}

The helper functions to validate the conversions to long and realloc could be written as:

/* reallocate long pointer memory */
long *realloc_long (long *lp, unsigned long *n)
{
    long *tmp = realloc (lp, 2 * *n * sizeof *lp);
    if (!tmp) {
        fprintf (stderr, "%s() error: reallocation failed.\n", __func__);
        // return NULL;
        exit (EXIT_FAILURE);
    }
    lp = tmp;
    memset (lp + *n, 0, *n * sizeof *lp); /* memset new ptrs 0 */
    *n *= 2;

    return lp;
}

note: you can adjust whether to return NULL or exit on memory exhaustion to fit your needs. For the conversion, you can use simple error checking with strtol as shown below.

/* simple strtol wrapper with error checking */
long xstrtol (char *p, char **ep, int base)
{
    errno = 0;

    long tmp = strtol (p, ep, base);

    /* Check for various possible errors */
    if ((errno == ERANGE && (tmp == LONG_MIN || tmp == LONG_MAX)) ||
        (errno != 0 && tmp == 0)) {
        perror ("strtol");
        exit (EXIT_FAILURE);
    }

    if (*ep == p) {
        fprintf (stderr, "No digits were found\n");
        exit (EXIT_FAILURE);
    }

    return tmp;
}

I have place a full example of how to read a file into a dynamically allocated 2D array of long at pastbin: C - read file into dynamically allocated 2D array

Upvotes: 1

Related Questions