views:

72

answers:

2

Hi Folks,

I am new to C, and want to read some data from a file.

Actually, I find many reading functions, fgetc, fgets, etc.. But I don't know which one/combination is the best to read a file with the following format:

0 1500 100.50
1 200     9
2 150     10

I just need to save each row above into a struct with three data members.

I just need to know the best practice to do that, hence I am new to C programming.

Thanks.

+5  A: 

Try reading each line using fgets. With each line, you can then use sscanf.

FILE* f = fopen("filename.txt", "r");
if (f) { 
    char linebuff[1024];
    char* line = fgets(linebuff, 1024, f);
    while (line != NULL) {
        int first, second;
        float third;
        if (sscanf(line, "%d %d %g", &first, &second, &third) == 3) {
            // do something with them.. 
        } else {
            // handle the case where it was not matched.
        }
        line = fgets(linebuff, 1024, f);
    }
    fclose(f);
}

This may have errors, but it's just meant to give you an example of how you might use the functions. Be sure to validate what sscanf returns you.

SB
So, why not using fscanf directly?
Mohammed
@Mohammed: because number formats in fscanf like %g skip whitespace including newlines. This will prevent you from checking that the file has three values per line.
lhf
I was going to suggest using `fscanf` also, but I like this advice better.
T.E.D.
@lhf - good point.
SB
It works, but when I replaced float with double, it returned garbage data in the last column, do you know why?
Mohammed
You may need to supply the format %lf (lowercase L f) instead of %g. What formatter did you use?
SB
The above code also accepts a line like `1 2 3 abc\n`, which shows that just checking the number of fields is not enough.
Roland Illig
it was meant to be an example and not a complete solution.
SB
+1  A: 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static void
read_file(const char *fname)
{
    FILE *f;
    char line[1024];
    int lineno, int1, int2, nbytes;
    double dbl;


    if ((f = fopen(fname, "r")) == NULL) {
        perror("fopen");
        exit(EXIT_FAILURE);
    }

    for (lineno = 1; fgets(line, sizeof line, f) != NULL; lineno++) {

        int fields = sscanf(line, " %d %d %lg %n", &int1, &int2, &dbl, &nbytes);
        if (fields != 3 || (size_t) nbytes != strlen(line)) {
            fprintf(stderr, "E: %s:%d: badly formatted data\n", fname, lineno);
            exit(EXIT_FAILURE);
        }

        /* do something with the numbers */
        fprintf(stdout, "number one is %d, number two is %d, number three is %f\n", int1, int2, dbl);
    }

    if (fclose(f) == EOF) {
        perror("fclose");
        exit(EXIT_FAILURE);
    }
}

int main(void)
{
        read_file("filename.txt");
        return 0;
}

Some notes on the code:

  • The fscanf function is quite difficult to use. I had to experiment a while until I got it right. The space characters between the %d and %lg are necessary so that any white-space between the numbers is skipped. This is especially important at the end of the line, where the newline character must be read.
  • Most of the code is concerned with checking errors thoroughly. Almost every return value of a function call is checked whether it succeeded or not. In addition, the number of fields and the number of characters that have been read are compared to the expected values.
  • The format strings for fscanf and fprintf differ in subtle details. Be sure to read the documentation for them.
  • I used the combination of fgets to read one line at a time and sscanf to parse the fields. I did this because it seemed impossible to me to match a single \n using fscanf.
  • I used the GNU C Compiler with the standard warning flags -Wall -Wextra. This helped to avoid some easy mistakes.

Update: I forgot to check that each invocation of fgets reads exactly one line. There might be lines that are too long to fit into the buffer. One should check that the line always ends with \n.

Roland Illig