Search code examples
c++visual-studiogccifstream

EOF vs basic_ios::eof() on GCC 5.2.1


On a program compiled with GCC 5.2.1, the state of ifstream doesn't switch to basic_ios::eof() when it hits the end of a file—namely, the condition if(eof()) evaluates to false after the end of file has been reached—whereas the same code compiled on Visual Studio 2015 behaves as expected: when the end of file is reached, the basic_ios::eof() evaluates to true in the if conditional.

I substituted the if(eof()) with if(bad()) and then with if(fail()), and all of them evaluated to false. However, when I place the EOF macro instead, the if(EOF) evaluates to true―just like the if(eof()) does on the program compiled by VS.

What may be the reason for std::basic_ios::eof() not to work on a program compiled with GCC?

PS: Below is the code of the program

#include <string>
#include <iostream>
#include <fstream>
#include <regex>

using namespace std;
using namespace std::chrono;

int ex10()
{
    ifstream in{ "school.txt" };
    if (!in) cout << "The 'school.txt' file was not opened.\n";

    string line;

    regex header{ R"(^([\w]+)(\t{2}\w+\s*\w*)(\t\w+\s*\w*)(\t\w+\s*\w*)$)" };
    regex row{ R"(^(\w+\s*\w*)(\t{1,2}\d+)(\t{2}\d+)(\t{2}\d+)$)" };

    if (getline(in, line)) {
        smatch matches;
        if (!regex_match(line, matches, header))
            cerr << "Wrong header format.\n";
    }
    int linenum = 0;
    int boys = 0;
    int girls = 0;

    ofstream out{ "schoolCompressed.txt" };
    if (!out) cout << "The output file was not created.\n";
    string prevLine;
    int accumBoys;
    int accumGirls;
    int accumTot;

    while (getline(in, line)) {
        ++linenum;
        smatch matches;
        if (!regex_match(line, matches, row))
            cerr << "Row #" << linenum << " doesn't match the format.\n";


        int curr_boy = stoi(matches[2]);
        int curr_girl = stoi(matches[3]);
        int curr_total = stoi(matches[4]);

        if (curr_boy + curr_girl != curr_total)
            cerr << "Wrong children number in line #" << linenum << '\n';

        if (line[0] != prevLine[0]) {

            if (linenum != 1) out << prevLine[0] << "\t\t" << accumBoys << "\t\t"
                << accumGirls << "\t\t" << accumTot << '\n';
            accumBoys = curr_boy;
            accumGirls = curr_girl;
            accumTot = curr_total;

        }

        else if (line[0] == prevLine[0]) {

            accumBoys += curr_boy;
            accumGirls += curr_girl;
            accumTot += curr_total;
        }


        if (EOF && curr_boy == boys && curr_girl == girls) { out << line; return 0; } //this works on GCC
        //if (in.eof() && curr_boy == boys && curr_girl == girls) { out << line; return 0; } <= this works on VS 2015


        boys += curr_boy;
        girls += curr_girl;
        prevLine = line;
    }
    cerr << "Somehow the program didn't manage to complete its task :(.\n";
    return 1;
}
int main()
{
    ex10();
}

Text of the school.txt file KLASSE DRENGE PIGER ELEVER 0A 12 11 23 1A 7 8 15 1B 4 11 15 2A 10 13 23 3A 10 12 22 4A 7 7 14 4B 10 5 15 5A 19 8 27 6A 10 9 19 6B 9 10 19 7A 7 19 26 7G 3 5 8 7I 7 3 10 8A 10 16 26 9A 12 15 27 0MO 3 2 5 0P1 1 1 2 0P2 0 5 5 10B 4 4 8 10CE 0 1 1 1MO 8 5 13 2CE 8 5 13 3DCE 3 3 6 4MO 4 1 5 6CE 3 4 7 8CE 4 4 8 9CE 4 9 13 REST 5 6 11 Alle klasser 184 202 386


Solution

  • EOF is a macro for a constant integer so it is not surprise that

     if (EOF && curr_boy == boys && curr_girl == girls)
    

    is "working". It is just not doing what you expect, which is checking end of file.

    You should use eof() only when an io operation has failed. The doc stipulates that

    std::basic_ios::eof only reports the stream state as set by the most recent I/O operation.it does not examine the associated data source. For example, if the most recent I/O was a get(), which returned the last byte of a file, eof() returns false. The next get() fails to read anything and sets the eofbit. Only then eof() returns true

    In your code you have

    while (getline(in, line)) {
       ...
       if (in.eof())
       ...
    }
    

    The expected behavior is the one of GCC. To know that you are at the end of the file you should attempt to read further. This is a similar issue to

    while (!in.eof()) {
       ...
       getline(in, line)
       ...
    }
    

    What you should is move curr_boy and the condition out of the while loop and eliminate the eof test

    int curr_boy = 0;
    int curr_girl = 0;
    while (getline(in, line)) {
    }
    
    if (curr_boy == boys && curr_girl == girls) { out << line; return 0; }