Search code examples
cscanffgetc

Using fscanf *after* fgetc issue


In C, there are many posts concerning using fgetc after fscanf, dealing with an additional \n, but I am seeing another issue when using them in the reverse order; fscanf after fgetc.

When using fscanf after fgetc, I get a different fscanf-result to if I just omit fgetc (in the example script, just hard-coding num=1000 and commenting-out the block using fgetc).

I can replicate this correct fscanf-result while still using fgetc if I rewrite the file contents to the myFile variable, as in the below script. Removing this line produces the different incorrect fscanf-result.

What is causing the difference in the fscanf-result when using fgetc first, and how can I address the issue?

/* First read tab-delimited 4-digit int data from a text file,
 * parsing into an array of the correct size num, then compute
 * the mean of the array while avoiding overflow. */

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

int main(){
    FILE *myFile;
    myFile = fopen("int_values.txt", "r");

    int c=0, i=0;
    float mean = 0.0;

    // Identifying there are 1000 values in the tab-delimited file.
    // Using fgetc
    int num = 1;
    while ((c=fgetc(myFile)) != EOF ){
        if (c == '\t')
            ++num;
    }

    int arr[num]; // Array of correct size for all values from file.

    // Redeclaring since fgetc seems to break fscanf
    myFile = fopen("int_values.txt", "r");

    // Read and store each value from file to array.
    for (i=0; i<num; i++){
        fscanf(myFile, "%d ", &arr[i]);
    }
    // Compute average cumulatively to avoid overflow.
    mean = arr[0]
    for (i=1; i<num; i++){
        //printf("In the %dth place, arr has value %d.\n", i, arr[i]);
        mean *= (float)i / (float)(i+1);
        mean += arr[i] / (float)(i+1);
    }
    
    fclose(myFile);

    printf("The overall mean of the %d values in the file is %f.\n\n", num, mean);
    return 0;
}

Solution

  • Identifying there are 1000 values in the tab-delimited file.

    Do not count tabs. Instead, read the ints. It's far too easy for the number of tabs to not relate correctly to the number of int.

    Sum the int into a long long to avoid overflow. Use double for generic floating-point math.

    #include <stdio.h>
    #include <stdlib.h>
    
    int main(void) {
      FILE *myFile = fopen("seal_weights.txt", "r");
      if (myFile) {
        int num = 0;
        long long sum = 0;
        int value;
        // return 1 on success, EOF on end-of-file, else 0 on non-numeric input
        while (fscanf(myFile, "%d", &value) == 1) {
          sum += value;
          num++;
        }
        double mean = num ? (double) sum / num : 0.0;
        printf("The overall mean of the %d values in the file is %f.\n\n", num,
            mean);
    
        // read in again and save values if truly desired.
        // This step not needed to compute average.
        rewind(myFile);
        int i;
        int arr[num];
        for (i = 0; i < num; i++) {
          if (fscanf(myFile, "%d", &arr[i]) != 1) {
            break;
          }
        }
        // Use arr[] in some way.
    
        fclose(myFile);
      }
      return 0;
    }