Search code examples
carraysstructscanffgets

Checking for null/empty float values when using sscanf


The following program attempts to read an input file line by line using fgets, and save each comma delimited float value into an array of structs using sscanf (this aspect of the code works fine). The issue lies in that the program should also detect when a float value is missing/empty, and assign it the float value 1.500 which then is saved into the array of structs.

EDIT: This is supposed to be compiled using VS2017, so on Windows.

*Note: Please note that the following questions have been studied before posting this question:

How to check if a string returned by scanf is null

How to get scanf to continue with empty scanset

An example of the input file (missing value in the second row):

0.123f, 0.234f, 0.345f, 0.456f, 0.567f
1.987f, , 7.376f, 2.356f, 5.122f
9.111f, 1.234f, 7.091f, 6.672f, 9.887f

Desired output (missing value in second row is detected and set to 1.500):

0.123 0.234 0.345 0.456 0.567
1.987 1.500 7.376 2.356 5.122
9.111 1.234 7.091 6.672 9.887

So far, the first attempt tried to scan all 5 floats (each with 'f' suffix) into strings and then check to see if those strings are null/empty or of zero length using strcmp and strlen, respectively, and finally involved trying to use sscanf again on each of those variables to read each into an array of structs.

The 2nd attempt included a check to see if the sscanf was successful by using if (sscanf(line, "%ff", &data[i].x) == NULL) { // ...some alert and assign 1.500}, which did not work either. The 3rd attempt, as seen below:

#include "stdio.h"

int main() {

typedef struct {
    float x, y, vx, vy, mass;
}DATA;

    FILE *file = fopen("null_detector.txt", "r");
    if (file == NULL)
    {
        printf(stderr, "ERROR: file not opened.\n");
        return EXIT_FAILURE;
    }
    int N= 3;
    DATA* data = malloc(Nbodies * sizeof * data); // Array allocation
    char line[256];
    int i;
    int inc = 1;
    for (i = 0; i < Nbodies; i += inc)
    {
        fgets(line, sizeof(line), file);

        // **Some info:
        // Scan 5 float variables per line (this part works fine)
        sscanf(line, "%ff, %ff, %ff, %ff, %ff",
            &data[i].x, &data[i].y, &data[i].vx, &data[i].vy, &data[i].mass); // %ff accounts for 'f' suffix

        // Now check if any of above vars are empty/NULL.
        // NOTE: aware that these vars CANNOT be compared to NULL,
        // but has been included to try and provide clarity for end goal
        if (data[i].x == NULL)
        {
            //.. assign 1.500 to data[i].x
        }
        if (data[i].y == NULL)
        {
            //... same as above etc
        }
        // ...Repeat IF statements for all 5 vars

    }

     //Print the contents of array of structs to check for correct output
    for (i = 0; i < Nbodies; i++)
    {
        printf("%.3f %.3f %.3f %.3f %.3f\n", data[i].x, data[i].y, data[i].vx, data[i].vy, data[i].mass);
    }

    return 0;
}

Summary:

Does anyone know how this program can be modified to:

  • detect missing float values in each line of the file upon reading them with fgets
  • replace missing float values with the float value 1.500
  • write these values to the array of structs, like the non-missing values successfully are doing?
  • As commented in the code, I am aware that the struct float variables cannot be compared to NULL. I have included this comparison in the code to only try to add some clarity as to what the end goal is.

Solution

  • You can use strsep to separate each line.

    str = strsep(&line, ",")
    

    Using one function to set the value of data:

    void set_data(DATA *dt, int count, float f) {
        switch(count) {
            case 0: dt->x = f; break;
            case 1: dt->y = f; break;
            case 2: dt->vx = f; break;
            case 3: dt->vy = f; break;
            case 4: dt->mass = f; break;
        }
    }
    

    The complete code:

    
    #include <stdio.h>  
    #include <unistd.h>  
    #include <string.h>  
    #include <stdlib.h> 
    
    typedef struct {
        float x, y, vx, vy, mass;
    }DATA;
    
    void set_data(DATA *dt, int count, float f) {
        switch(count) {
            case 0: dt->x = f; break;
            case 1: dt->y = f; break;
            case 2: dt->vx = f; break;
            case 3: dt->vy = f; break;
            case 4: dt->mass = f; break;
        }
    }
    
    int main() {
    
        FILE *file = fopen("text.txt", "r");
        if (file == NULL)
        {
            printf( "ERROR: file not opened.\n");
            return EXIT_FAILURE;
        }
        int N= 3;
        DATA* data = malloc(N * sizeof(data)); // Array allocation
        char *line;
        int i;
        int inc = 1;
        size_t n = 0;
        for (i = 0; i < N; i += inc)
        {
            getline(&line, &n, file);
            int count = 0;
            char *str;
            while((str = strsep(&line, ",")) != NULL) {
                if (strcmp(str, " ") == 0) {
                    set_data(&data[i], count, 1.5);
                } else {
                    set_data(&data[i], count, atof(str));
                }
               // printf("count = %d\n", count);
                // printf("token: %s\n", str);
                count++;
            }
    
        }
    
         //Print the contents of array of structs to check for correct output
        for (i = 0; i < N; i++)
        {
            printf("%.3f %.3f %.3f %.3f %.3f\n", data[i].x, data[i].y, data[i].vx, data[i].vy, data[i].mass);
        }
    
        return 0;
    }
    

    The input:

    #cat text.txt
    0.123f, 0.234f, 0.345f, 0.456f, 0.567f
    1.987f, , 7.376f, 2.356f, 5.122f
    9.111f, 1.234f, 7.091f, 6.672f, 9.887
    

    The output:

    0.123 0.234 0.345 0.456 0.567
    1.987 1.500 7.376 2.356 5.122
    9.111 1.234 7.091 6.672 9.887