Probably the easiest single-pass approach is simple to read the floats in the file into an allocated block of memory, that can be expanded as needed with realloc
until all values are read. Then you can simply loop and determine if the number of values read will form a square matrix -- handle the error if they won't. You then have an efficiently stored block of floats that with simple arithmetic can be manipulated as a 2D array. For example, if you read 9 values, and your square size is 3x3, you can access your i, j
row and column values as:
array [i * 3 + j];
(note: another option -- you can allocate n
pointers and n
additional blocks of n
- floats if you want true 2D indexing, e.g. array [i][j]
-- up to you)
A simple implementation that does just that could be:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NELEM 16 /* initial number of floats to allocate */
/** realloc 'ptr' of 'nelem' of 'psz' to 'nelem * 2' of 'psz'.
* if *nelem == 0, initial allocation of NELEM psz block,
* returns pointer to reallocated block of memory with new
* memory initialized to 0/NULL. return must be assigned to
* original pointer in caller.
*/
void *xrealloc2 (void *ptr, size_t psz, size_t *nelem)
{
void *memptr = NULL;
if (!*nelem)
*nelem = NELEM < 2 ? 2 : NELEM / 2;
memptr = realloc (ptr, *nelem * 2 * psz);
if (!memptr) {
perror ("realloc(): virtual memory exhausted.");
exit (EXIT_FAILURE);
/* return NULL; */
} /* zero new memory (optional) */
memset ((char *)memptr + *nelem * psz, 0, *nelem * psz);
*nelem *= 2;
return memptr;
}
int main (int argc, char **argv) {
float *arr = NULL; /* pointer to allocate block of floats */
size_t n = 0, /* number of floats read */
nelem = 0, /* number allocated */
sqsize = 0; /* square size of 2D matrix */
/* 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;
}
for (;;) { /* loop continually reading float from file */
if (n == nelem) /* is realloc needed? */
arr = xrealloc2 (arr, sizeof *arr, &nelem); /* realloc 2x current */
if (fscanf (fp, "%f", &arr[n]) != 1) { /* read/validate float */
if (n) /* if values stored, break loop */
break;
else /* otherwise, error or EOF before values read */
exit (EXIT_FAILURE);
}
n++;
}
if (fp != stdin) /* close file if not stdin */
fclose (fp);
while (sqsize * sqsize < n) /* determine size of square matrix */
sqsize++;
if (sqsize * sqsize != n) { /* validate only that number read */
fputs ("error: n not a square.\n", stderr);
exit (EXIT_FAILURE);
}
for (size_t i = 0; i < sqsize; i++) { /* output matrix */
for (size_t j = 0; j < sqsize; j++)
printf (" %2g", arr[i * sqsize + j]);
putchar ('\n');
}
free (arr); /* free allocated memory */
}
Since you are reading one float
at a time, it doesn't matter whether the numbers are all in one line, or one per-line, or arranged in square-matrix form, fscanf()
skips whitespace whether that be a space or newline.
Exampe Use/Output
2x2:
$ echo "1.1 2.2 3.3 4.4" | ./bin/array_1d2d_square_float
1.1 2.2
3.3 4.4
non-square:
$ echo "1.1 2.2 3.3 4.4 5.5" | ./bin/array_1d2d_square_float
error: n not a square.
3x3:
$ echo "1.1 2.2 3.3 4.4 5.5 6.6 7.7 8.8 9.9" | ./bin/array_1d2d_square_float
1.1 2.2 3.3
4.4 5.5 6.6
7.7 8.8 9.9
9x9 from file with 1-81:
$ ./bin/array_1d2d_square_float dat/81int.txt
1 2 3 4 5 6 7 8 9
10 11 12 13 14 15 16 17 18
19 20 21 22 23 24 25 26 27
28 29 30 31 32 33 34 35 36
37 38 39 40 41 42 43 44 45
46 47 48 49 50 51 52 53 54
55 56 57 58 59 60 61 62 63
64 65 66 67 68 69 70 71 72
73 74 75 76 77 78 79 80 81