First points:
while(!feof(file))
{
You will want to look at Why is while ( !feof (file) ) always wrong?. Further, the entire outer loop is superfluous to your code and can simply be removed.
With any of the scanf
family of functions, when input does not match the conversion specifier used in the format string, a matching failure occurs, character extraction from the input stream stops at the point of failure, the offending characters causing the failure are left in the input stream (unread) just waiting to bite you on your next attempted read. Attempting to read 'a'
as type int
with scanf
produces a matching failure.
How To Handle A Matching Failure?
Since you always validate all user input, as you note, you detect an input failure with scanf
when the return is less than the number of conversions specified (or EOF
). Reading an integer at a time with fscanf
within your loops tells you exactly where the input failure happened from a row/col standpoint when reading a square matrix worth of data. Knowing where the failure occurred you can output the row/col where the invalid data was encountered.
To capture the invalid data for reporting purposes, you can simply scan forward with fgetc
reading a character at a time and filling a buffer with the offending characters until the next valid digit
or +/-
(explicit sign for a digit) is encountered (you can use ungetc
at that point to put the valid digit (or sign) back in the input stream if your wanted to continue reading additional values)
A Short Example
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#define MAXC 1024
int main (int argc, char **argv) {
int **m = NULL;
unsigned dim = 0;
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
if (fscanf (fp, "%u", &dim) != 1) { /* read square mtrx dim */
fputs ("error: invalid format, no dimension.\n", stderr);
return 1;
}
if (!(m = malloc (dim * sizeof *m))) { /* allocate/validate dim ptrs */
perror ("malloc-m");
return 1;
}
for (unsigned i = 0; i < dim; i++) /* allocate dim rows of dim int */
if (!(m[i] = calloc (dim, sizeof *m[i]))) { /* zero mem w/calloc */
perror ("calloc-m[i]");
return 1;
}
for (unsigned i = 0; i < dim; i++) /* for each row */
for (unsigned j = 0; j < dim; j++) { /* for each col */
if (fscanf (fp, "%d", &m[i][j]) != 1) { /* read/validate int */
char buf[MAXC], *p = buf; /* buf and ptr to buf */
int c; /* int for char */
/* read while !EOF, not digit and not -/+ */
while ((c = fgetc(fp)) != EOF && (c < '0' || '9' < c) &&
c != '-' && c != '+')
if (!isspace(c)) /* if not a space */
*p++ = c; /* store offending char(s) */
*p = 0; /* nul-terminate buf */
if (c != EOF) /* if c a char - put it back */
ungetc(c, fp);
/* output location of invalid input */
printf ("error: m[%d][%d] - invalid entry '%s'.\n",
i, j, buf);
return 1; /* and bail - or continue -- your choice */
}
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
for (unsigned i = 0; i < dim; i++) { /* for each row */
for (unsigned j = 0; j < dim; j++) /* for each col */
printf (" %3d", m[i][j]); /* output value */
putchar ('\n'); /* output newline */
free (m[i]); /* free ints in row */
}
free (m); /* free pointers */
return 0;
}
(note: you didn't say how you allocated storage, so a simple pointer to pointer to int
was used to allocate dim
pointers and a block of dim
integers was allocated and the starting address for each allocation assigned to each of the pointer to allow indexing as a 2D array)
Example Input Files
Using your example input with invalid int
at [1][1]
:
$ cat ~/tmpd/mtrx.txt
3
12 3 87
78 a 9
45 0 23
An example with good values:
$ cat ~/tmpd/mtrxgood.txt
3
12 3 87
78 8 9
45 0 23
Example Use/Output
With invalid int
at [1][1]
:
The program correctly reports the location and the offending character:
$ ./bin/readsquaremtrx ~/tmpd/mtrx.txt
error: m[1][1] - invalid entry 'a'.
With good values, all values are read and the matrix printed to the screen before all allocated memory is freed:
$ ./bin/readsquaremtrx ~/tmpd/mtrxgood.txt
12 3 87
78 8 9
45 0 23
The code is commented to help you follow along. If you have any questions, just let me know.