1

i have a file.txt structured this way: author, "title", genre, price, copies_inStock

R. Tolkien, "The lords of the rings", Fantasy, 65.50, 31

i tried using fgets and sscanf

FILE *fp = NULL;
    char string[100];
    char title[30], author[30], genre[30];
    float price;
    int copies=0;
   fp = fopen("text.txt", "r");
   while(!feof(fp)) {
       fgets(string, 100, fp);
       sscanf(string, "%[^,],%[^,],%[^,],%f[^,],%d[^ ]", autore, titolo, genere, &prezzo, &copie);
   }
fclose(fp);
printf("%s %s %s %.2f %d\n", author, title, genre, price, copies);
OUTPUT
R. Tolkien  "The lord of the rings"  fantasy 65,50 0

Why it don't access to the variable copies? There are better ways? Thanks

Domnik
  • 11
  • 2
  • Put a space in front of `"%[^,]"` so it is `" %[^,]"`. That format specifier does not automatically filter whitespace, unlike `"%f"` and `"%d"`. Similarly with `"%c"` which you don't use here. – Weather Vane Sep 14 '21 at 08:04
  • Note: you forgot to translate `autore, titolo, genere, prezzo, copie` to `author, title, genre, price, copies` in the `sscanf` line. I'm not sure if the names mismatch in your original program too, or if you just translated them for this StackOverflow question. Note 2: please, avoid naming a variable as `string`, it will give you a lot of headaches when you use external libraries/code. – Luca Polito Sep 14 '21 at 08:07
  • Note that `"%f[^,]"` and `"%d[^,]"` are improperly formed, use `"%f,"` and `"%d"`. – Weather Vane Sep 14 '21 at 08:08
  • 1
    *There are better ways?* You're going to run into problems if you want to allow titles with embedded commas (eg "The good, the bag, and the ugly"). You need a better parser than `sscanf()` for that. – pmg Sep 14 '21 at 08:10
  • 2
    Another note: you may want to read [why `while (!feof(file))` is always wrong](https://stackoverflow.com/questions/5431941/why-is-while-feof-file-always-wrong). – Luca Polito Sep 14 '21 at 08:10
  • @pmg +1 for the movie reference and for pointing out the comma problem, however there's a bug: it's `bad`, not `bag`. – Luca Polito Sep 14 '21 at 08:15
  • Yes i forgot translates the variable for stackoverflow, sorry. @WeatherVane Thank you very much, it works. But if would to ignore "...." from the title? i just can't add them directly to %[^,"]. There are better or more secure ways? – Domnik Sep 14 '21 at 08:18
  • Using `strtok()` or one of its variants would be better. As mentioned, commas in the title will be a problem. – Weather Vane Sep 14 '21 at 08:20
  • @LucaPolito Thanks, i'll read your 3d, and then edit my program with the improvements. – Domnik Sep 14 '21 at 08:21
  • Drive the loop with the return value `while(fgets(...) != NULL)`. Generally, you should put more focus on the return value that I/O functions give you. Note: please don't update *this* question with your improvements. – Weather Vane Sep 14 '21 at 08:22
  • @WeatherVane Ok, and thank you. – Domnik Sep 14 '21 at 08:36
  • Note that both `fgets()` and `sscanf()` can fail. If a line is longer than the 100 characters you allow for, `string` will not end with a newline, and a partial line will sit in `fp`. If the file (or your format string...) is malformed, `sscanf()` will return with a matching failure -- check that return value! – DevSolar Sep 14 '21 at 15:43

2 Answers2

0

The format specifiers on this line are incorrect

sscanf(string, "%[^,],%[^,],%[^,],%f[^,],%d[^ ]", autore, titolo, genere, &prezzo, &copie);

It should be

sscanf(string, "%[^,], %[^,], %[^,],%f,%d", autore, titolo, genere, &prezzo, &copie);

The additional spaces are to filter leading whitespace - it is not automatic with %[] (or with %c).

The %f and %d were a sort of mangled hybrid of what they should be. The conversion of those stops at the first character that cannot be used, without your intervention.

Side note: you really must check the result of scanf() function family: the number of successful conversions made.

Weather Vane
  • 33,872
  • 7
  • 36
  • 56
  • Ok, i edited my code with your suggestions and now works, but the "....." creates a lot of problems, like you (and other says) i have to use strtok, but i never heard about this function. How it works? i have to create a const char delim = "," and use it for skipping those chars? after i skipped the " and the , then i use sscanf on the string for saving parsed string to the variabiles? – Domnik Sep 14 '21 at 08:35
  • The `...` in my comment was just a placeholder for the *actual* function arguments. Here is a man page for [`strtok()`](https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/strtok-strtok-l-wcstok-wcstok-l-mbstok-mbstok-l?view=msvc-160) with an example. – Weather Vane Sep 14 '21 at 08:36
0

"%f[^,]" is legal, yet certainly not what OP wants.

"%f" scans for a float, then "[^,]" scans for that 4 character sequence.


There are better ways?

Use " %n" to check scanning success. It records the offset of the scan.
Use width limits like 29 in %29[^,] to not overfill the char array.
Use a space before %29[^,] to consume optional leading whitespace.
Money is always tricky.
Do not use while(!feof(fp)). Check return from fgets().

char string[100];
char title[30], author[30], genre[30];
double price;
int copies;
FILE *fp = fopen("text.txt", "r");

if (fp) {
  while(fgets(string, sizeof string, fp)) {
    int n = 0;
    sscanf(string, " %29[^,], %29[^,], %29[^,],%lf ,%d %n", 
        author, title, genre, &price, &copies, &n);
    // If n==0, scan was incomplete
    // If string[n], string has extra garbage
    if (n == 0 || string[n]) {
      fprintf(stderr, "Bad <%s>\n", string);
    } else {
      printf("%s %s %s %.2f %d\n", author, title, genre, price, copies);
      // Robust code here would do additional work: 
      // Trim trailing string whitespace.  Range checks on numeric values, etc.
    }
  }
  fclose(fp);
}
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256