Reputation: 3
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
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