Reputation: 39
This is my function to read a matrix from a file. In the text file I have on the first line 2 is n
and 3 is m
and on the next two lines is the matrix.
I don't have erros, but my program "stop working" and I don't know why. Thank you!
In main I have: readmatrix(n,m,a, "text.txt");
int readmatrix(int* n,int *m, int a[*n][*m], char* filename){
FILE *pf;
int i,j;
pf = fopen (filename, "rt");
if (pf == NULL)
return 0;
fscanf (pf, "%d",n);
for (i=0;i<*n;i++)
{
for(j=0;j<*m;j++)
fscanf (pf, "%d", &a[i][j]);
}
fclose (pf);
return 1;
}
Upvotes: 3
Views: 20887
Reputation: 101
If you're not afraid of using goto
, an easy-to-read, robust way to read a matrix looks like this:
typedef struct Matrix
{
size_t width, height;
double **data;
} Matrix;
Matrix read_matrix(char const *filename)
{
Matrix matrix;
FILE *file;
if ((file = fopen(filename, "rt")) == NULL)
goto error;
if (fscanf(file, "%zu %zu", &matrix.width, &matrix.height) != 2)
goto error_file;
if ((matrix.data = (double **)calloc(matrix.height, sizeof(double *))) == NULL)
goto error_file;
for (size_t y = 0; y < matrix.height; ++y)
if ((matrix.data[y] = (double *)malloc(matrix.width * sizeof(double))) == NULL)
goto error_matrix;
for (size_t y = 0; y < matrix.height; ++y)
for (size_t x = 0; x < matrix.width; ++x)
if (fscanf(file, "%lf", &matrix.data[y][x]) != 1)
goto error_matrix;
fclose(file);
return matrix;
error_matrix:
for (size_t y = 0; y < matrix.height; ++y)
free(matrix.data[y]);
free(matrix.data);
error_file:
fclose(file);
error:
return (Matrix){0, 0, NULL};
}
Upvotes: 0
Reputation: 13580
If your compiler supports VLAs or you are using C99, then you can do this:
#include <stdio.h>
int readmatrix(size_t rows, size_t cols, int (*a)[cols], const char* filename)
{
FILE *pf;
pf = fopen (filename, "r");
if (pf == NULL)
return 0;
for(size_t i = 0; i < rows; ++i)
{
for(size_t j = 0; j < cols; ++j)
fscanf(pf, "%d", a[i] + j);
}
fclose (pf);
return 1;
}
int main(void)
{
int matrix[2][3];
readmatrix(2, 3, matrix, "file.dat");
for(size_t i = 0; i < 2; ++i)
{
for(size_t j = 0; j < 3; ++j)
printf("%-3d ", matrix[i][j]);
puts("");
}
return 0;
}
file.dat
looks like this:
1 2 3
4 5 6
and the output of my program is
$ ./a
1 2 3
4 5 6
Note that this is a basic example, you should always check the return value of
fscanf
. If file.dat
had one row only, then you would get in trouble. Also
there are not numbers in the file, you would get also undefined values in the
matrix.
I'd advice to read the whole line with fgets
and then parse the line using
sscanf
or some other function like strtok
, then it would be easier to react
to errors in the input file.
edit
A more robust way of reading a file like this would be:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int **readmatrix(size_t *rows, size_t *cols, const char *filename)
{
if(rows == NULL || cols == NULL || filename == NULL)
return NULL;
*rows = 0;
*cols = 0;
FILE *fp = fopen(filename, "r");
if(fp == NULL)
{
fprintf(stderr, "could not open %s: %s\n", filename, strerror(errno));
return NULL;
}
int **matrix = NULL, **tmp;
char line[1024];
while(fgets(line, sizeof line, fp))
{
if(*cols == 0)
{
// determine the size of the columns based on
// the first row
char *scan = line;
int dummy;
int offset = 0;
while(sscanf(scan, "%d%n", &dummy, &offset) == 1)
{
scan += offset;
(*cols)++;
}
}
tmp = realloc(matrix, (*rows + 1) * sizeof *matrix);
if(tmp == NULL)
{
fclose(fp);
return matrix; // return all you've parsed so far
}
matrix = tmp;
matrix[*rows] = calloc(*cols, sizeof *matrix[*rows]);
if(matrix[*rows] == NULL)
{
fclose(fp);
if(*rows == 0) // failed in the first row, free everything
{
fclose(fp);
free(matrix);
return NULL;
}
return matrix; // return all you've parsed so far
}
int offset = 0;
char *scan = line;
for(size_t j = 0; j < *cols; ++j)
{
if(sscanf(scan, "%d%n", matrix[*rows] + j, &offset) == 1)
scan += offset;
else
matrix[*rows][j] = 0; // could not read, set cell to 0
}
// incrementing rows
(*rows)++;
}
fclose(fp);
return matrix;
}
int main(void)
{
size_t cols, rows;
int **matrix = readmatrix(&rows, &cols, "file.dat");
if(matrix == NULL)
{
fprintf(stderr, "could not read matrix\n");
return 1;
}
for(size_t i = 0; i < rows; ++i)
{
for(size_t j = 0; j < cols; ++j)
printf("%-3d ", matrix[i][j]);
puts("");
}
// freeing memory
for(size_t i = 0; i < rows; ++i)
free(matrix[i]);
free(matrix);
return 0;
}
Now file.dat
looks like this:
1 2 3 4
4 5 6 5
9 8 8 7
5 5 5 5
1 1 1 1
And the output is
1 2 3 4
4 5 6 5
9 8 8 7
5 5 5 5
1 1 1 1
In this example I calculate the number of columns only for the first column and use that number for all other columns. If the input file has rows with less columns that the first row, then the missing values are stored with 0. If it has rows with more columns than the row will be trimmed.
I calculate the number of rows like this:
while(sscanf(scan, "%d%n", &dummy, &offset) == 1)
{
scan += offset;
(*cols)++;
}
First I declare a pointer scan
to point to line
, so that I can modify the
pointer without losing the original line. The %n
in sscanf
is not counted in
the number of successfull conversions, this returns the position of scan
where
it stopped reading. I used that to loop the sscanf
. I explicitly check that
sscanf
returns 1 and if that's the case, I increment the number of columns and
I update scan
to update to the point where sscanf
stoppped reading. This
allows me to continue scanning until the end of the line is reached. I use a
similar technique to parse all the integers.
Upvotes: 5