Search code examples
c++fgetsscanf

fscanf line with condition


my goal is to read in a data file consisting of just one number per line and write the data into a histogram. There are some comments in the file behind # characters. I want to skip these lines.

I have started writing:

TH1F *hist = new TH1F("hist","",4096, -0.5,4095.5);
//TF1 *fitfunc;

char filename[100];
double val;
int i;
char line[256];


sprintf(filename,"test.dat");
FILE* pfile = fopen(filename, "r");

for (i=0;i<=14;i++) {
    fgets(line,256,pfile);
    cout<<line<<endl;
    fscanf(pfile, "%lf /n", &val);
    hist->SetBinContent(i,val);
}

But only every other line gets written as "line" while the others are fscanfed.

Would be very nice, if someone could give me a hint.

...so this will obviously not work properly:

TH1F *hist = new TH1F("hist","",4096, -0.5,4095.5);
//TF1 *fitfunc;

char filename[100];
double val;
int i;
char zeile[256];

sprintf(filename,"test.dat");
FILE* pfile = fopen(filename, "r");

for (i=0;i<=14;i++) 
{
    fgets(zeile,256,pfile);
    cout<<"fgets: "<<zeile<<endl;
    if (zeile[0]!='#')
    {
        fscanf(pfile, "%lf /n", &val);
        cout<<"val: "<<val<<endl;
        hist->SetBinContent(i,val);
    }
}

Solution

  • You need to use sscanf() instead of fscanf() after you've read the line with fgets():

    TH1F *hist = new TH1F("hist", "", 4096, -0.5, 4095.5);
    char filename[100];
    char zeile[256];
    
    sprintf(filename, "test.dat");
    FILE *pfile = fopen(filename, "r");
    if (pfile == 0)
        …handle error; do not continue…
    
    for (int i = 0; i < 14 && fgets(zeile, sizeof(zeile), pfile) != 0; i++) 
    {
        cout << "fgets: " << zeile << endl;
        if (zeile[0] != '#')
        {
            double val;
            if (sscanf(zeile, "%lf", &val) == 1)
            {
                cout << "val: " << val << endl;
                hist->SetBinContent(i, val);
            }
            // else … optionally report that line was erroneous
        }
    }
    

    I left the sprintf() for the file name in place, but it provides marginal value. I'd be tempted to use const char *filename = "test.dat"; so that the error message can report the file name that failed to open without repeating the string literal.

    Converted into a standalone test program:

    #include <cstdlib>
    #include <iostream>
    
    using namespace std;
    
    int main()
    {
        char filename[100];
        char zeile[256];
    
        sprintf(filename, "test.dat");
        FILE *pfile = fopen(filename, "r");
        if (pfile != 0)
        {
            for (int i = 0; i < 14 && fgets(zeile, sizeof(zeile), pfile) != 0; i++) 
            {
                cout << "fgets: " << zeile;
                if (zeile[0] != '#')
                {
                    double val;
                    if (sscanf(zeile, "%lf", &val) == 1)
                        cout << "val: " << val << endl;
                }
            }
            fclose(pfile);
        }
        return 0;
    }
    

    and given a test data file test.dat containing:

    1.234
    2.345
    #3.456
    #4.567
    5.678
    

    the output from the program shown is:

    fgets: 1.234
    val: 1.234
    fgets: 2.345
    val: 2.345
    fgets: #3.456
    fgets: #4.567
    fgets: 5.678
    val: 5.678
    

    This generates the three expected val lines and reads but ignores the two comment lines.