0

I'm having problems using a fscanf function. It never reads it correctly and always results in blanks.

fscanf(f, " %[^;];%[^;];%d;%d;%d;%d;%[^;];%d;%[^\n]",
            arr[i].loc1, arr[i].loc2, &arr[i].price, &arr[i].rooms, 
            &arr[i].bathroom, &arr[i].carpark, arr[i].type, &arr[i].area, arr[i].furnish);

The code above always outputs " 0 0 0 0 0". But when I try using a scanf and manually input one of the lines, it works perfectly.

The file it's reading from is a .csv file. Here is the contents:

Mont-Kiara;Kuala-Lumpur;1000000;2;2;0;Built-up;1000;Partly

Cheras;Kuala-Lumpur;310000;3;2;0;Built-up;1000;Partly

Kepong;Kuala-Lumpur;358000;3;3;0;Built-up;1000;Partly

Taman-Desa;Kuala-Lumpur;455000;2;2;0;Built-up;1000;Partly

Kepong;Kuala-Lumpur;358000;3;3;0;Built-up;1000;Partly

Kepong;Kuala-Lumpur;358000;3;3;0;Built-up;1000;Partly

And here is the full code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct houseData {
    
    char loc1[101];
    char loc2[101];
    int price[101];
    int rooms[101];
    int bathroom[101];
    int carpark[101];
    char type[101];
    int area[101];
    char furnish[101];
    
} arr[1001];

int read() {

    int i = 0;
    struct houseData arr[800];
    char temp1[100];
    
    FILE *f = fopen("file.csv", "r");

    while(!feof(f)){
        
        fscanf(f, " %[^;];%[^;];%d;%d;%d;%d;%[^;];%d;%[^\n]"
        , &arr[i].loc1, &arr[i].loc2, &arr[i].price, &arr[i].rooms, 
        &arr[i].bathroom, &arr[i].carpark, &arr[i].type, &arr[i].area, &arr[i].furnish);
        i++;
    }

    fclose(f);
}

int main() {

    read();

    printf("%s %s %d %d %d %d %s %d %s", arr[i].loc1, arr[i].loc2, *arr[i].price, *arr[i].rooms, *arr[i].bathroom, *arr[i].carpark, arr[i].type, *arr[i].area, arr[i].furnish);

    return 0;   
}

Gerhardh
  • 11,688
  • 4
  • 17
  • 39
lazerbs
  • 21
  • 2
  • Why are your integer fields arrays with 101 elements? It makes no sense considering your data. – user694733 Jan 16 '23 at 13:08
  • Why do you have arrays for the integers? And for strings, the pointer-to operator `&` is wrong (arrays *decay* to pointers to their first element, which will be of the correct type for strings, using `&` will give the wrong type). – Some programmer dude Jan 16 '23 at 13:08
  • 4
    Please see [Why is `while ( !feof (file) )` always wrong?](http://stackoverflow.com/questions/5431941/why-is-while-feof-file-always-wrong), and aside: please see [What is the effect of trailing white space in a scanf() format string?](https://stackoverflow.com/questions/19499060/what-is-the-effect-of-trailing-white-space-in-a-scanf-format-string) – Weather Vane Jan 16 '23 at 13:08
  • 1
    You also seem to have a misunderstanding about variable scope and life-time, since `arr` is a local variable inside the the `read` function, and won't be available in the `main` function. – Some programmer dude Jan 16 '23 at 13:09
  • 2
    You must always check return value of `scanf` family functions to make sure that all fields are converted. – user694733 Jan 16 '23 at 13:09
  • 2
    All in all it almost seems like you have skipped some rather important parts of your beginners learning material, or have some basic misunderstanding about what it means. I suggest you take some time to refresh and study it a little bit more. If you have something you don't understand, then please ask your teacher about this specific issue, or if you don't have a teacher (or friend) you can ask then you're welcome back here to ask about that specific thing. – Some programmer dude Jan 16 '23 at 13:11
  • 4
    You can probably get this to work, but be aware that `scanf` and `fscanf` are generally suitable/appropriate only for the simplest, toy programs. For a problem like this, it's probably easier to read whole lines using `fgets`, break lines up into fields using `strtok`, and finally convert numeric fields using `atoi` or the like. – Steve Summit Jan 16 '23 at 13:12
  • 1
    Your code results in compiler errors and warnings. Please copy&paste exactly the code you ran on your system and fix all compiler warnings. If you don't see the warnings you might have to enable the warnings. – Bodo Jan 16 '23 at 13:12
  • 1
    The posted code does not compile, so it cannot do as you say. Please post a [Minimal Reproducible Example](https://stackoverflow.com/help/minimal-reproducible-example), the shortest *complete* code that shows the problem. The best way to do that is by copy/paste. – Weather Vane Jan 16 '23 at 13:13
  • 1
    Your fundamental problem is that `%[^;]` reads a *string*, not an `int`. So even after you change `int price[101];` to the `int price;` that you probably actually want, you cannot use `%[^;]` to read into it. (But with that said, I don't recommend trying to use `%[^;]` at all. `%[…]` is for sophisticated problems, but as I said, `scanf` and `fscanf` are really only suitable for very simple problems.) – Steve Summit Jan 16 '23 at 13:13
  • Change the loop to `while( fscanf(...) == 9 )`. You do not want to process a line that doesn't parse correctly. Using `fscanf` will make it much more difficult to emit an error message that accurately describes the location of the input error, but it is probably sufficient to just mention the line number. – William Pursell Jan 16 '23 at 13:33

1 Answers1

1

There are a lot of issues in your code. Here's a version that may help. The error messages in this version are far from ideal (this does not distinguish between an input format error and a error reading data, for example, nor does it provide much detail on the location of the error), and there is still the possibility of undefined behavior on certain inputs, (see Is `scanf("%d", ...)` as bad as `gets`?) but this should point you in the right direction. Well, at least it may help to improve your use of scanf, but a very reasonable argument can the be made that the "right direction" is to stop using scanf completely. It is very difficult to get things right with scanf, and attempting to do so winds up being much more complex that just using fgets. But for simple use cases it is ... still pointless to use scanf. See http://sekrit.de/webdocs/c/beginners-guide-away-from-scanf.html and many other resources that explain why scanf is a terrible choice.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct houseData{
        char loc1[101];
        char loc2[101];
        int price;
        int rooms;
        int bathroom;
        int carpark;
        char type[101];
        int area;
        char furnish[101];
};

int
read(FILE * f, struct houseData *h)
{
        return 9 == fscanf(f, " %100[^;]; %100[^;]; %d; %d; %d; %d; "
                "%100[^;]; %d; %100[^\n]", h->loc1, h->loc2, &h->price,
                 &h->rooms, &h->bathroom, &h->carpark, h->type, &h->area,
                 h->furnish);
}

int
main(int argc, char **argv)
{
        int rv;
        FILE *f = argc > 1 ? fopen(argv[1], "r") : stdin;
        if( f == NULL ){
                perror(argv[1]);
                return EXIT_FAILURE;
        }
        struct houseData h;
        int i = 0;
        while( read(f, &h) ){
                printf("%d: %s %s %d %d %d %d %s %d %s\n", ++i,
                         h.loc1, h.loc2, h.price, h.rooms, h.bathroom,
                         h.carpark, h.type, h.area, h.furnish);
        }
        if( ! feof(f) ){
                fprintf(stderr, "Error near line %d\n", i);
        }
        fclose(f);
}
William Pursell
  • 204,365
  • 48
  • 270
  • 300
  • Minor: Space before `"%d;"`, as in `" %d;"` is not needed, maybe it looks nicer. OTOH, `"%d ;"` , with a space after the `"%d"` would be more generous in accepting input. – chux - Reinstate Monica Jan 16 '23 at 16:53
  • `int read()` returns 0 or 1 and code later uses `if( ! feof(f) )` to distinguish. Yet that logic has some corner holes like failing to read a final `%100[^\n]` before end-of-file. Better if `int read()` returned 1, EOF and 0 on failure. Still a good demo of trying to show why reading stopped. – chux - Reinstate Monica Jan 16 '23 at 17:21