-2

I'm reading from an external file values but fscanf wont read it correctly

I have my information in an external .c file in the form [char float int\n char float int\n...]but for some reason using fscanf in a while loop as fscanf(time, "%s %.2f %d\n", temp, tempp, temps); everything is saved in the temp variable so when i try to print it the results are

name: tetris, price: 0.00, sold: 0
name: 9.99, price: 0.00, sold: 0

the saved file looks like this

Tetris 9.99 4
Wormgame 4.50 5

my struct is


    struct game {
        char *name;
        float price;
        int sold;
    };

function that reads


    struct game * open_file(char * tiedosto) {
        char* filen = strcat(tiedosto, ".c");
        struct game *newarray = malloc(sizeof(struct game));
        newarray[0].name = NULL;
        FILE* time = fopen(tiedosto, "r");
        if (!time) {
            return NULL;
        }
        int characters = 0;
        int linechange = 0;
        while ((characters = fgetc(time)) != EOF) {
            if (characters == '\n') {
                linechange++;
            }
        }
        rewind(time);
        char temp[100];
        float tempp;
        int temps;
        int i;
        for ( i = 0; i < linechange; i++) {
            fscanf(time, "%s %.2f %d\n", &temp, &tempp, &temps); // this is where the problem lies 
            newarray = add_peli(newarray, temp, tempp);
            newarray[i].sold = temps;

        }
        fclose(time);
        print_items(newarray, i);
        return newarray;
    }

and lastly the function that adds the name to the struct


    struct game *add_peli(struct game* array, char* nimi, float hinta )
    {
        int i;
        for (i = 0; array[i].name != NULL; i++) {
            if (array[i].name != NULL) {
                if (strcmp(array[i].name, nimi) == 0) {
                    printf("Game already exists\n");
                    return array;
                }
            }
        }
        struct game* newarray = realloc(array, sizeof(struct game) * (i + 2));

        newarray[i].name = malloc(sizeof(char) * (strlen(nimi)+1));
        strcpy(newarray[i].name, nimi);
        newarray[i].price = hinta;
        newarray[i].sold = 0;
        newarray[i + 1].name = NULL;
        return newarray;
    }

as mentioned it for some reason just returns the right amount of games as in if I insert two games and then save the file it will only return two instances

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
vNdong
  • 9
  • 3
  • 1
    One suggestion: *always* check the return value of `fscanf`. One common problem is that an earlier call has failed, and the unparseable input has been left on the input, causing all later calls to fail, too. If `fscanf` fails, and you ignore its return value telling you so, your variables (`temp`, `tempp`, `temps`) won't have been filled in with anything, but you won't realize it. – Steve Summit Aug 23 '19 at 13:37
  • 1
    What happens if the last line doesn't end with a newline? – Some programmer dude Aug 23 '19 at 13:40
  • And if you already know the number of records (which is equal to the number of lines) then why allocate and reallocate? Just allocate *once*. – Some programmer dude Aug 23 '19 at 13:41
  • @Someprogrammerdude they should all end with newline as it is written to the .c file with fpritf("%s %f %d\n") – vNdong Aug 23 '19 at 13:44
  • does it work with the format string `"%s %f %d\n"` instead of `"%s %.2f %d\n"` ? – Sander De Dycker Aug 23 '19 at 13:46
  • 2
    Oh and almost *never* should you have a trailing white-space (like newline) in a `scanf` format string. It's almost always wrong. It's not the problem here, but please remove it. – Some programmer dude Aug 23 '19 at 13:46
  • Wow @SanderDeDycker that worked, I feel really stupid now... – vNdong Aug 23 '19 at 13:49
  • @Someprogrammerdude I'll change it, thanks! – vNdong Aug 23 '19 at 13:49
  • If you want the intellectual satisfaction of solving this problem with `fscanf` (of which there are dozens), then by all means, proceed. But, if you'd just like to read structured input from a file, reliably, and get on with your life, and never use the *scanf family again, then you can instead read whole lines with `fgets`, and break them into whitespace-separated "words" using `strtok` or the like, and convert the numeric ones using `strtol` and `strtod` (or `atoi` and `atof`), and be done with it. – Steve Summit Aug 23 '19 at 13:50
  • As an alternative to the method described by @SteveSummit, you could read the lines with `fgets` (as he mentions) but then use `sscanf` to "parse" the input. At least this way it will be easier to handle lines with errors. – Some programmer dude Aug 23 '19 at 13:52
  • For the most part, you need to take *no action* with whitespace in the input when using the `scanf` function family. Most of the formats automatically filter out leading whitespace. Some exceptions are `%c` and `%[]` and `%n`. – Weather Vane Aug 23 '19 at 13:53
  • And now that it's working, you might try it with the input line `chess 99.99 none` and see what happens... – Steve Summit Aug 23 '19 at 13:53
  • The question has way too much code. In the end you would just have needed **one file** with **one** line in it and 5 lines to demonstrate the problem. – Antti Haapala -- Слава Україні Aug 23 '19 at 13:57
  • Also trust me - it is better to not mix Finnish and English in variable names :D – Antti Haapala -- Слава Україні Aug 23 '19 at 13:59
  • `char* filen = strcat(tiedosto, ".c");` is suspect too - how array pointed to by `tiedosto` had suitably 2 extra characters for the suffix? – Antti Haapala -- Слава Україні Aug 23 '19 at 14:01

2 Answers2

2

While "%.2f" is a valid format specifier for printf, it isn't for scanf. Use "%f" instead, ie. :

fscanf(time, "%s %f %d\n", temp, &tempp, &temps);
Sander De Dycker
  • 16,053
  • 1
  • 35
  • 40
0

fscanf(time, "%s %.2f %d\n", &temp, &tempp, &temps); // this is where the problem lies

First, lose the & operator on &temp - since it's an array, the expression temp will be converted to char * before being passed to scanf.

Second, lose the \n at the end of the format string - it's not necessary ("%s" will skip over any leading whitespace) and having it there means fscanf will block until it sees an explicit \n in the input stream, which could cause problems if the last line in the file isn't followed by a newline character.

Also make sure everything was read correctly:

if ( fscanf( time, "%s %f %d", temp, &tempp, &temps ) == 3 )
{
  // process as normal
}
or
{
  // input not formatted as expected, handle as appropriate
}
John Bode
  • 119,563
  • 19
  • 122
  • 198
  • *"First, lose the `&` operator on `&temp` - since it's an array, the expression `temp` will be converted to `char *` before being passed to `scanf`."* -- Correct, but you may want to mention why it "just works". – S.S. Anne Aug 23 '19 at 13:59
  • 1
    Having the `"\n"` at the end does not mean it will "block until it sees an explicit `\n`". It simply means it will consume any amount (including zero) of whitespace characters (same effect as using `" "`). Or in standardese : "A directive composed of white-space character(s) is executed by reading input up to the first non-white-space character (which remains unread), or until no more characters can be read. The directive never fails." – Sander De Dycker Aug 23 '19 at 14:30
  • See [Trailing white space in a `scanf()` format string](https://stackoverflow.com/questions/19499060/) — the trailing white space (newline) isn't satisfied until there is something other than white space in the input. It will read a thousand newlines if you just hit return. You have to type the start of the next input, or indicate EOF, to terminate the reading. Yes, get rid of the newline; no, not for the reason given. – Jonathan Leffler Aug 23 '19 at 14:57