Whenever you have to handle multiple types as a single object, you should be thinking struct to provide storage. Here you have two integers and a float you want to store. A simple struct allowing that would be:
typedef struct { /* struct to hold data from line */
int n1, n2;
float f;
} twointf;
The typedef is for convenience allowing you to refer to twointf as the type instead of repeating struct twointf each time. With a struct, you can now declare a simple array of twointf to handle as many as needed.
To read you data, reading an entire line with fgets() into into a sufficiently sized buffer (character array) and parsing with sscanf() is always preferred over a direct read with fscanf(). If there is an error in your data file, fgets() will read the entire line into the buffer and the error will only effect a single line of data. With fscanf() a Matching-Failure can occur, or your read will wrap to the next line with an omitted value corrupting your read from that point forward in the file.
To read with fgets() and separate with sscanf() you can do:
#define MAXC 1024 /* if you need a constant, #define one (or more) */
#define MAXS 32
...
char buf[MAXC]; /* read buffer for line */
size_t ndx = 0; /* array of struct index */
twointf arr[MAXS] = {{ .n1 = 0 }}; /* array of struct */
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
...
/* while array not full, read line */
while (ndx < MAXS && fgets (buf, MAXC, fp)) {
if (sscanf (buf, "%d.%d.%f", /* separate into 2 int and float */
&arr[ndx].n1, &arr[ndx].n2, &arr[ndx].f) == 3) {
ndx++; /* on success, increment index */
}
}
That's all there is to it. Read your data, convert with sscanf() validating the return and only incrementing your array index on successful conversion of all values.
Putting it altogether you could do:
#include <stdio.h>
#define MAXC 1024 /* if you need a constant, #define one (or more) */
#define MAXS 32
typedef struct { /* struct to hold data from line */
int n1, n2;
float f;
} twointf;
int main (int argc, char **argv) {
char buf[MAXC]; /* read buffer for line */
size_t ndx = 0; /* array of struct index */
twointf arr[MAXS] = {{ .n1 = 0 }}; /* array of struct */
/* 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;
}
/* while array not full, read line */
while (ndx < MAXS && fgets (buf, MAXC, fp)) {
if (sscanf (buf, "%d.%d.%f", /* separate into 2 int and float */
&arr[ndx].n1, &arr[ndx].n2, &arr[ndx].f) == 3) {
ndx++; /* on success, increment index */
}
}
if (fp != stdin) /* close file if not stdin */
fclose (fp);
for (size_t i = 0; i < ndx; i++) /* output results */
printf ("\narr[%zu].n1 = %d\narr[%zu].n2 = %d\narr[%zu].f = %.2f\n",
i, arr[i].n1, i, arr[i].n2, i, arr[i].f);
}
Example Use/Output
With your sample data in dat/data_int_float.txt, you would have:
$ ./bin/twointf dat/data_int_float.txt
arr[0].n1 = 115
arr[0].n2 = 41
arr[0].f = 24.36
arr[1].n1 = 113
arr[1].n2 = 49
arr[1].f = 44.00
arr[2].n1 = 115
arr[2].n2 = 41
arr[2].f = 51.57
(you can use the %g format specifier for floating-point to only output the decimal places if fractional data exists -- up to you)