I am trying to read from a file with this text inside:
f 502 601 596 465 464
f 597 599 600 598
f 602 591 596 601
f 588 565 548 260 62 61 595
f 583 595 61 558 561
f 237 241 471
On each line, there is an f followed by a random amount of floats. I want to be able to take the numbers after the f and store them in an array of structs of floats. I wrote code that will parse the text file if there is three floats on each line but now I am instructed to do it if there is a random amount of floats (up to 13 floats). Basically the code I have now for three floats on every line is as follows:
struct faces {
float p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12;
}
struct faces eFace[100];
FILE *fp;
char buff[128];
int fCount = 0;
fp = fopen("text.txt", "r");
if (fp == NULL)
printf("Can't open file");
else {
while (fgets(buff, sizeof(buff), fp) != NULL) {
if (buff[0] == 'f') {
sscanf(buff, "f %f %f %f", &eFace[fCount].p1, &eFace[fCount].p2, &eFace[fCount].p3);
fCount++;
}
}
}
fclose(fp);
}
What would be the best way to modify my code so that it takes every float (up to 13 floats) after "f" until a new line and stores them in the array of the structs I made? I appreciate any help and if you need more information just let me know!
Note: I always have to check if the line starts with an f.
Since you have an upper bound to the number of floats, use that, but do not hard-code the 13.
Scan the file twice to determine the number of lines.
Use fgets()
to read each line.
Use sscanf()
or strtod()
to read each number. Be sure to perform error checking.
Untested code follows:
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <float.h>
#define faces_N 13
typedef struct faces {
float p[faces_N];
} faces_T;
void foo(void) {
faces_T *eFace = NULL;
size_t Lines = 0;
FILE *fp;
fp = fopen("text.txt", "r");
if (fp == NULL)
printf("Can't open file");
else {
char buff[2*(faces_N*(FLT_DIG + 10) + 1 + 1)];
while (fgets(buff, sizeof(buff), fp) != NULL)
Lines++;
rewind(fp);
eFace = malloc(Lines * sizeof(*eFace));
assert(eFace);
for (size_t l = 0; l < Lines; l++) {
if (fgets(buff, sizeof(buff), fp) != NULL)
Handle_IOError();
if (buff[0] != 'f')
Handle_FormatError();
size_t f;
char *p = &buff[1];
for (f = 0; f < faces_N; f++) {
int n;
if (sscanf(p, "%f%n", &eFace[l].p[f], &n) != 1)
break;
p += n;
}
// TBD what to do with other
for (; f < faces_N; f++) {
eFace[l].p[f] = 0.0; // Or maybe NaN or save count in eFace
}
}
fclose(fp);
// do whatever with eFaces;
}
free(eFaces);
}
The size of buff
is debatable. I like 2x the maximum expected size and would throw an error if the buffer was full.
The number of char
to expect for a float
should be able to distinguish all float
. Estimating via " -D.DDDDDDddde-dd"
, came up 1 (space) + 1 (sign) + 1 (.) + FLT_DIG + 3 (see link) + 4 (exponent)
. See the link for a discussion about printing precision: width specifier to maintain precision of floating-point value