What is difference in checking eof() on getline i.e. while(!getline().eof()) and checking simply while(getline())

Viewed 342

What is difference between while(getline()) & while(!getline().eof()) ?

Am parsing an input string. I have tried with both condition check and I see the difference in result.

std::string testStr = "CN=Test, OU=XYZ, O=ABC";
std::stringstream inpStrStream(testStr);
std::string tempStr;
getline(inpStrStream, tempStr, '=');
std::cout<<"Hello:"<<tempStr<<std::endl;

//Let's call this as "Ex A"
while(getline(inpStrStream, tempStr, '='))
{
   std::cout<<tempStr<<std::endl;
}
    (OR)
//Let's call this as "Ex B"
while(!getline(inpStrStream, tempStr, '=').eof())
{
   std::cout<<tempStr<<std::endl;
}

I expected same result in both but the result for "Ex A" & "Ex B" are different. I don't see string "ABC" in output of "Ex B":

Ex A result: Hello:CN Test, OU XYZ, O ABC

Ex B result: Hello:CN Test, OU XYZ, O

3 Answers

Ref.: CppReference.com, "iostate"

getline() returns a reference of the stream it is operating on, i.e. inpStrStream.

inpStrStream.operator bool() (or while ( inpStrStream ) / while ( getline( inpStrStream, tempStr, '=' ) ) ) will check if failbit or badbit of the stream are set.

! inpStrStream.eof() will check if eofbit of the stream is set. (*)

CppReference states,

...in nearly all situations, if eofbit is set, failbit is set as well.

You've hit one of the exceptions here. .operator!() checks for failbit, not for eofbit -- ant that getline() of "ABC" does set eofbit (as EOF of the stream is reached), but not failbit (as that last operation was still successful). This makes .eof() end the loop (not printing "ABC"), while .operator!() will do one more iteration (printing "ABC"), trying another getline(), which fails (as there is nothing more to read), setting failbit, and ending the loop.

So... .eof() will explicitly test for EOF only, i.e. will try to continue even if something nasty has happened to the underlying stream that did get one of the other two flags set.


(*): Note that there are more tests. The only symmetric ones are .fail() and .operator!() (which test the same thing), and their symmetric opposite .operator bool(). The others -- .good(), .bad(), and .eof() -- check different things each!

Take a look on this simple test code:

#include <iostream>
#include <sstream>

#define LOG(x) std::cout << __LINE__ << ": " #x " = " << x << '\n'

void test(char termChar)
{

    std::stringstream input;
    input << "lets try this!";
    std::string s;
    std::getline(input, s, termChar);
    
    LOG(!!input);
    LOG(input.eof());
    LOG(input.tellg());
    char ch = '?';
    
    input >> ch;
    LOG(!!input);
    LOG(input.eof());
    LOG(input.tellg());
    LOG(ch);
}

int main()
{
    test('!');
    std::cout << '\n';
    test('#');
    
    return 0;
}

And its output: https://godbolt.org/z/f55eTjWK1

14: !!input = 1
15: input.eof() = 0
16: input.tellg() = 14
20: !!input = 0
21: input.eof() = 1
22: input.tellg() = -1
23: ch = ?

14: !!input = 1
15: input.eof() = 1
16: input.tellg() = -1
20: !!input = 0
21: input.eof() = 1
22: input.tellg() = -1
23: ch = ?
  1. Remember eof doesn't mean that you are at the end of stream, but that you have tried to read stream beyond its size.
  2. Now in second run of test getline was successful (!!input) but there was attempt to read beyond stream size so eof returns true. Now your loop with eof can reject things which are important for you since eof is set to true but reading was a success.

The class std::stringstream inherits the class std::basic_ios that defines the operator

explicit operator bool() const;

that

1 Returns: !fail().

This operator is used in the condition of the while statement

while(getline(inpStrStream, tempStr, '=')) 

to contextually convert the object returned by the call of std::getline to the type bool.

From the C++ Standard (C++ 17, 7 Standard conversions)

4 Certain language constructs require that an expression be converted to a Boolean value. An expression e appearing in such a context is said to be contextually converted to bool and is well-formed if and only if the declaration bool t(e); is well-formed, for some invented temporary variable t (11.6).

Related