c++fgetsfseekftell

fseek() sets the pointer to wrong position


I have a .dat file with the following structure:

Object name | Area | Length |Form Factor

I am trying the find which object has the minimum area. I read the whole file and keep track of the object with the minimum area, and its position with ftell(). When I am done reading the file I set stream position indicator to whatever I get from ftell(). Then I read that line as my final result, but right now, I am reading the wrong line.

objects.dat:

toy1 12785.00 550.70 23.22
toy2 11506.50 495.63 0.48
Pen 35008.65 1450.98 0.17
Pencil 42788.35 1773.42 0.209
card7 128433.00 1552.67 0.67

Code:

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

using namespace std;

int main()
{
    FILE *cfPtr;
    errno_t err;

    char name[15];
    double area, length, fF;
    double minArea = 100000.0;
    long address, minAreaAdd;

    char string[100];

    if ((err = fopen_s(&cfPtr, "objects.dat", "r")) != 0)   // Check if we can reach the file
        printf("The file 'objecs.dat' was not opened to read.\n");
    else
    {
        while (fgets(string, 100, cfPtr)) 
        {
            address = ftell(cfPtr);
            sscanf_s(string,"%s %lf %lf %lf", &name, sizeof name, &area, &length, &fF);

            if (area < minArea)
            {
                minArea = area;
                minAreaAdd = address;
            }
        }

        cout << "Min Area: " << minArea << " Address: " << minAreaAdd << "\n\n";

        fseek(cfPtr, minAreaAdd, SEEK_SET);
        fgets(string, 100, cfPtr);
        sscanf_s(string, "%s %lf %lf %lf", &name, sizeof name, &area, &length, &fF);

        cout << "Name: " << name << " Area : " << area << " Length: " << length << " Form factor: " << fF << "\n";

        fclose(cfPtr);
    }
    return 0;
}

Output:

Min Area: 11506.5 Address: 55

Name: Pen Area : 35008.7 Length: 1450.98 Form factor: 0.17

Solution

  • As mentioned by "1201ProgramAlarm", you are getting the address after you have read the line you want to save. That is, you use ftell after fgets.

    What happens is that fgets moves the file position indicator while reading the data. This is because fgets needs to prepare the file for the next read operation, otherwise you would be stuck at the same position, reading the same data.

    You need to reverse this logic. Here goes one example:

    address = -1
    do
    {
        if (address >= 0) {
            sscanf_s(string,"%s %lf %lf %lf", &name, sizeof name, &area, &length, &fF);
    
            if (area < minArea)
            {
                minArea = area;
                minAreaAdd = address;
            }
        }
        address = ftell(cfPtr);
    } while (ret = fgets(string, 100, cfPtr));