1

I am using fscanf to get a name and a number from a file in this format and print it out:

   Jim: 100.00
  John: 200.00
  Adam: 300.00
Miguel: 400.00

My output is not being printed properly.

I've tried using while(fscanf(filep, "%s %lf", name, balance) != EOF) but it always seems to print in this format:

   Jim:: 0.0000
  John:: 0.0000
  Adam:: 0.0000
Miguel:: 0.0000

It seems that it reads the name correctly, but it adds the colon at the end of the name (I have a colon in the printf, so I would like to avoid the extra colon) but it doesn't read the numbers correctly. I'm not really sure what I can do to fix this, so help would be appreciated.

gholt12
  • 35
  • 4
  • `while(fscanf(filep, "%s %lf", name, balance) != EOF)` is insufficient. Post a [mcve]. – chux - Reinstate Monica Mar 19 '23 at 04:31
  • `fscanf()` is including the colon in the string. If you're getting a double colon, it's because you're adding another one in the `printf()`. Don't do that. – Barmar Mar 19 '23 at 04:31
  • 1
    Remember, `%s` uses whitespace as the delimiter of the string. So the `:` is part of the name. – Barmar Mar 19 '23 at 04:33
  • As it looks like this is your first time using `scanf`, you might want to read [these guidelines for using it safely and correctly](https://stackoverflow.com/questions/72178518#72178652). (Your problem is rule #1 on that list.) – Steve Summit Mar 20 '23 at 21:25
  • *it reads the name correctly, but it adds the colon at the end of the name*, no, it read a string just like you asked it to. *You* added the "extra" colon, in your `printf` call. :-) – Steve Summit Mar 20 '23 at 22:07

3 Answers3

5

Here are some indications to fix your program:

  • to read the name before the :, you can use %[^:]

  • to avoid a potential buffer overflow, you should specify the maximum number of characters to store into the destination array:

    char name[30];
    scanf("%29[^:]", name);
    
  • to skip the pending newline and potential initial whitespace, add a space at the start of the format string:

    char name[30];
    scanf(" %29[^:]", name);
    
  • to consume the :, just add a : after the conversion format.

  • to parse the number, use the %lf conversion if balance has type double, but pass the address of the variable with the &:

    char name[30];
    double balance;
    scanf(" %29[^:]: %lf", name, &balance);
    
  • why not use & for name you might ask... because name is an array: passing an array as an argument to a function implicitly passes a pointer to its first element. You could do this explicitly as &name[0] but it is simpler and idiomatic to just pass name.

  • you should check that the conversions succeeded: scanf() returns the number of successful conversions, hence it will return 2 if the input was parsed correctly.

  • your output shows Miguel:: 0.0000 because the : was read as part of the name with %s and the balance was printed with a %.4f format. The first problem is solved thanks to the %29[^:] conversion, for the second you should use %.2f to output 2 decimals.

  • To make it easier to detect the end of file and recover from invalid input, it is recommended to read one line at a time with fgets() and use sscanf() to attempt to convert the contents of the line.

Here is a function to parse the file:

#include <stdio.h>

int read_file(FILE *fp) {
    char line[200];
    char name[30];
    double balance;
    int count = 0;

    while (fgets(line, sizeof line, fp)) {
        if (sscanf(line, " %29[^:]: %lf", name, &balance) == 2) {
            count++;
            printf("%s: %.2f\n", name, balance);
        } else {
            printf("invalid input: %s\n", line);
        }
    }
    return count; // return the number of lines converted
}

Note that this method will accept and ignore extra contents after the balance on the line. To detect and report this problem, you can use the %n conversion to retrieve the number of characters read and report the problem if the line has extra non whitespace contents:

#include <stdio.h>

int read_file(FILE *fp) {
    char line[200];
    int count = 0;

    while (fgets(line, sizeof line, fp)) {
        char name[30];
        double balance;
        int len = 0;

        if (sscanf(line, " %29[^:]: %lf %n", name, &balance, &len) == 2) {
            count++;
            printf("%s: %.2f\n", name, balance);
            if (line[len] != '\0') {
                printf("extra characters: %s\n", line + len);
            }
        } else {
            printf("invalid input: %s\n", line);
        }
    }
    return count; // return the number of lines converted
}
chqrlie
  • 131,814
  • 10
  • 121
  • 189
2

As the posted code isn't complete (the variable definition is missing) we can't know for sure but

while(fscanf(filep, "%s %lf", name, balance) != EOF)

is wrong for a number of reasons.

  1. Checking for EOF is insufficient

  2. balance is (most likely) a double but %lf requires a double pointer

  3. %s may lead to buffer overflow. Always use a width specifier for %s

Something like:

char name[100];
double balance;
... open file, etc ...

while(1)
{
    int res = fscanf(filep, "%99s %lf", name, &balance);
    if (res == 2)
    {
        // Fine - print values
    }
    else if (res == EOF)
    {
        // Done
        break;
    }
    else
    {
        // Error: Didn't read 2 values
        // Add error handling...
    }
}

That said, I'll recommend using fgets followed by sscanf for better control .

Notice that use of %99s will result in : being part of name. You can add code to remove it (if needed). Also take a look at the answer from Barmar https://stackoverflow.com/a/75780112/4386427`It has an alternative to %99s that may be useful for you.

Support Ukraine
  • 42,271
  • 4
  • 38
  • 63
0

Change your scanf() format string so the : is not included in the name being read:

scanf(" %[^:]: %lf", name, &balance)

The space at the beginning is needed to skip over the initial whitespace. And you need & before balance.

Support Ukraine
  • 42,271
  • 4
  • 38
  • 63
Barmar
  • 741,623
  • 53
  • 500
  • 612
  • 3
    Er, shouldn’t that have a width in there somewhere (to prevent buffer overflow)? Also, you forgot a `%`. – Dúthomhas Mar 19 '23 at 04:46
  • @Barmar I added a `%` before `[^:]` as I'm sure it was just a typo. I didn't add a width specifier as that would be too many changes. – Support Ukraine Mar 19 '23 at 07:45