Search code examples
cfileintegerfgetsscanf

C - reading multiple integers and floats with sscanf


I am doing a practice problem for my C programming class which tells me to write a program that reads in variables from a file. On the first line, it is supposed to read in an integer N.

From there, it should read an integer, and then five floating points on every line for N lines. It is supposed to calculate a sum of all the floats in the file and write it to another file.

I have written a program that should do this using fgets to copy a line to a string, and sscanf to dissect it and assign the segments to different array locations. However, I am encountering some issue with obtaining extraneous information through sscanf (perhaps a null value or the newline). It is not storing the integer N properly (it is producing large random values and creating a runtime error by infinite loop), and it probably isn't working inside of the loop either.

How can I perfect this so that it reads in the integers and floats properly?

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

#define MAX_STRING 30
#define MAX_LINE_SIZE 200

int main(void)
{
    FILE *f1, *f2;
    char filename[MAX_STRING];
    char fileline[MAX_LINE_SIZE];
    int N, eN;
    float totalwage = 0;
    int* ssn;
    float** wage;

    printf ("Enter a file name for data analysis: ");
    scanf ("%s", &filename); //get file name from user input for reading

    f1 = fopen (filename, "r");

    fgets (fileline, MAX_LINE_SIZE, f1); //read first line
    sscanf (fileline, "%d", &N); //pull integer from first line to determine how many lines follow

    for (eN = 0; eN < N; eN ++) //read N lines following the first
    {
        // VVV read single line from file
        fgets (fileline, MAX_LINE_SIZE, f1);
        // VVV record data from line
        sscanf (fileline, "%d, %f, %f, %f, %f, %f", &ssn[eN], &wage[eN][0], &wage[eN][1], &wage[eN][2], &wage[eN][3], &wage[eN][4]);
        // VVV add the 5 wages on each line to a total
        totalwage += wage[eN][0] + wage[eN][1] + wage[eN][2] + wage[eN][3] + wage[eN][4];
    }

    fclose (f1);

    printf ("Enter a file name for the result: ");
    scanf ("%s", &filename); //get file name from user input for writing

    f2 = fopen (filename, "w");

    fprintf (f2, "%f", totalwage); //store total of wages in file specified by user

    printf ("\nThe information has been stored. Press any key to exit.\n");
    getchar();
}

The file being read is 'wages.txt' and it's contents are as follows:

10
1, 10, 20, 30, 40, 50
2, 11, 12, 13, 14, 15
3, 21, 23, 25, 27, 29
4, 1, 2, 3, 4, 5
5, 30, 60, 90, 120, 150
6, 37, 38, 39, 40, 41
7, 40, 50, 60, 70, 80
8, 5, 10, 15, 20, 25
9, 80, 90, 100, 110, 120
10, 1000, 2000, 3000, 4000, 2000

As a recap, the issue is that there is a runtime error in which the program crashes due to some sort of infinite loop. Through some debugging I saw that it was not reading in the first line as an integer properly. Instead of the value ten, it was storing large values as though it read a null character.


I have added code in attempt to allocate memory for ssn and wages. However, I am not sure if it was done correctly, and the program still has a crashing runtime error.

ssn = malloc (N*MAX_STRING);
wage = malloc (N*MAX_STRING);
for (eN = 0; eN < N; eN ++)
{
    wage[eN] = malloc (N*MAX_STRING);
}

Solution

  • As pointed out by others

    scanf ("%s", &filename);
    

    should be:

    scanf ("%s", filename);
    

    Note that sscanf is a big problem if the number of columns change

    Revised for use with strtod:

    #include <stdio.h>
    #include <stdlib.h>
    
    #define MAX_STRING 30
    #define MAX_LINE_SIZE 200
    #define COLS 5
    
    int main(void)
    {
        FILE *f1, *f2;
        char filename[MAX_STRING];
        char fileline[MAX_LINE_SIZE];
        int N, eN, i;
        float totalwage = 0;
        float (*wage)[COLS];
        char *p;
    
        printf ("Enter a file name for data analysis: ");
        scanf ("%s", filename); //get file name from user input for reading
        f1 = fopen (filename, "r");
        fgets (fileline, MAX_LINE_SIZE, f1); //read first line
        sscanf (fileline, "%d", &N); //pull integer from first line to determine how many lines follow
        wage = malloc((sizeof(float) * COLS) * N);
        /* check malloc ... */
        for (eN = 0; eN < N; eN ++) //read N lines following the first
        {
            // VVV read single line from file
            p = fgets (fileline, MAX_LINE_SIZE, f1);
            strtol(p, &p, 10); /* skip first column */
            for (i = 0; i < COLS; i++) {
                wage[eN][i] = strtod(p + 1, &p); /* p+1 skip comma, read a float */
                totalwage += wage[eN][i]; /* sum */
            }
        }
        free(wage);
        fclose (f1);
        printf ("Enter a file name for the result: ");
        scanf ("%s", filename); //get file name from user input for writing
        f2 = fopen (filename, "w");
        fprintf (f2, "%f", totalwage); //store total of wages in file specified by user
        printf ("\nThe information has been stored. Press any key to exit.\n");
        getchar();
        fclose(f2); /* you forget to close f2 */
        return 0;
    }
    

    Also note that you are reading floats only for sum, there is no need to store them, so malloc can be avoided, and in the real world check the result of fopen, fgets ...