1

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.

Kyle Asaff
  • 274
  • 3
  • 13
  • In C++, you'd probably want to read a line into a string, create a `stringstream` of the line, then read a char followed by a `std::vector` of floats from the stringstream (and ignore the char). – Jerry Coffin Mar 30 '14 at 21:21
  • oops sorry, this is for C only. I removed just removed the tag for C++ – Kyle Asaff Mar 30 '14 at 21:24
  • In while loop sscanf(" %f",...) and check what sscanf returns if - 0 then you know that your read wasn't succesful, so you;re probably at the next line;) – Ardel Mar 30 '14 at 21:30
  • Using a structure instead of an array to hold the up-to-13 values is a disaster...you need a loop, and a loop needs an array. Are you sure you wouldn't be better off with the structure holding an array of 13 floats? – Jonathan Leffler Mar 30 '14 at 21:30
  • 1
    possible duplicate of [how to use sscanf in loops?](http://stackoverflow.com/questions/3975236/how-to-use-sscanf-in-loops) – Jonathan Leffler Mar 30 '14 at 21:32
  • You current code doesn't check that the line starts with an `f`; it simply assumes that it does and that there will be three floating point values after it. You have to check the return value from `sscanf()` to ensure that the correct number of conversions takes place. – Jonathan Leffler Mar 30 '14 at 21:35
  • I probably would be better off, I just pasted my old code to show what I have to work with. It does check if it starts with an f. It takes the line from text.txt and put it in buff. it then checks if buff[0] is == to f and if it is then it stores the floats – Kyle Asaff Mar 30 '14 at 22:13

1 Answers1

2

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

Community
  • 1
  • 1
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • Consider up voting all answers that you felt helped you well (on this and your other questions). After some time (hours, days - your call) on your various questions, consider "accepting" an answer by clicking the check mark should it well solve your post. (I see you have accepted 0 for 4) – chux - Reinstate Monica Mar 31 '14 at 20:48