0

EDIT: there was a subtle error in my original test program code: the " char=" << aStream.peek() line (and probably the " input pos=" << aStream.tellg(), as well) modified the stream state-flags, so not the real state was reported. So those calls must be completely deleted from the code, otherwise we cannot see the real effect of seekg() on the state-flags. However the result is still the same: the eofbit is not set.

Original post:

I try to detect EOF of an std::istream by advancing the input pointer with 1 step by calling

seekg( 1, std::ios_base::cur )

However seekg() moves 1 position beyond EOF, when it sets the failbit of the stream. The eofbit is never set. See the output of this test program:

#include <iostream>
#include <sstream>

using namespace std;

void info( int aRelativePos, istream& aStream )
{
    cout << "POS=" << aRelativePos <<
            " input pos=" << aStream.tellg() <<
            " char=" << aStream.peek() <<
            "\tGood: " << aStream.good() <<
            " Eof: " << aStream.eof() <<
            " Bad: " << aStream.bad() <<
            " Fail: " << aStream.fail() << "\n";
}

int main()
{
    istringstream   input ("12");

    int i=0;
    while ( input.good() )
    {
        info( i, input );
        input.seekg( 1, std::ios_base::cur ); //advance 1 step forward
        ++i;
    }
    info ( i, input );

    return 0;
}

Output:

POS=0 input pos=0 char=49   Good: 1 Eof: 0 Bad: 0 Fail: 0
POS=1 input pos=1 char=50   Good: 1 Eof: 0 Bad: 0 Fail: 0
POS=2 input pos=-1 char=-1  Good: 1 Eof: 0 Bad: 0 Fail: 0
POS=3 input pos=-1 char=-1  Good: 0 Eof: 0 Bad: 0 Fail: 1

(Compiled by gcc 5.2 with -std=c++11. You can run this code here: http://coliru.stacked-crooked.com/a/69f4d70e93359423 )

Moreover MS document on seekg ( https://msdn.microsoft.com/en-us/library/y2d6fx99(v=vs.120).aspx ) says that relative positioning in text files is not supported by the C++ Standard.

But I could not find such info in the Standard. Can you please give me the reference?

Leslie N
  • 115
  • 11

1 Answers1

2

Well as long as you are okay with the standard says this is the behavior we have from [istream.unformatted]

basic_istream<charT,traits>& seekg(pos_type pos);

Effects: Behaves as an unformatted input function (as described in 27.7.2.3, paragraph 1), except that the function first clears eofbit, it does not count the number of characters extracted, and it does not affect the value returned by subsequent calls to gcount(). After constructing a sentry object, if fail() != true, executes rdbuf()->pubseekpos(pos, ios_base::in). In case of failure, the function calls setstate(failbit) (which may throw ios_base::failure).

So per the standard we will always clear the eofbit and on an failure only the fail bit is set. Trying to read past the end of failure is a failure so that is why it gets set. Just reaching the end of file is not a failure as the end is a valid position.

You can see in this example(modified from your code) that once we reach the end of file we are still good and then trying to read from there will not only set the eofbit but also the failbit as we are at the end of file and the extraction fails

input.seekg(0, std::ios::end);
info (input);
char ch;
input >> ch;
info (input);

Output:

Good: 1 Eof: 0 Bad: 0 Fail: 0
Good: 0 Eof: 1 Bad: 0 Fail: 1

Live Example

Community
  • 1
  • 1
NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • @NathanOlivier : you explain only why `eofbit` is cleared at the beginning of `seekg()`. For me it's not clear what the Standard means by "In case of failure, the function calls setstate(failbit)". Is reaching EOF a failure? When POS=2 the input pointer is passed the last char of the stream (see Output above), yet `failbit` is 0. There is no valid input at this position, so following the Standard I could infer that `failbit` is set then. But that is not what happens. – Leslie N Feb 25 '16 at 14:50
  • @LeslieN Going past the end of file is a failure. Reaching the end of file is not a failure. – NathanOliver Feb 25 '16 at 14:55
  • Note that I get the same results when I use the absolute positioning version of `seekg()`, i.e. `input.seekg( i )`. So in case of `i == 2`, the `failbit` is not set. – Leslie N Feb 25 '16 at 15:17
  • @LeslieN The "end" of the stream is one past the last element just like the `end` iterator to standard containers. so with `i == 2` we are at the end which is okay. We just cant go farther or do anything with it. – NathanOliver Feb 25 '16 at 15:21
  • @NathanOlivier : Can you please quote where it is defined regarding to `seekg()`? BTW the Standard says that `seekg()` call with relative positioning is in fact a `rdbuf()->pubseekoff(off, dir, std::ios_base::in)` call, that is in turn a `seekoff(off, dir, std::ios_base::in)` call. But I could not find any definition in the Standard how these functions change the state flags, when they reach or pass EOF... – Leslie N Feb 25 '16 at 15:36
  • @LeslieN I believe that is described by 27.8.2.4(11) *If (newoff + off) < 0, or if newoff + off refers to an uninitialized character (as defined in 27.8.2.3 paragraph 1), the positioning operation fails. Otherwise, the function assigns xbeg + newoff + off to the next pointer xnext.* and then 27.8.2.3 has *[...]then the resultant basic_string contains the character sequence in the range [pbase(),high_mark), where high_mark represents the position one past the highest initialized character in the buffer.* – NathanOliver Feb 25 '16 at 15:49
  • @LeslieN According to [this](http://coliru.stacked-crooked.com/a/731ab1138a81e4c3) it still does not set the `eofbit`. – NathanOliver Feb 25 '16 at 16:16
  • @LeslieN I updated the example to only get the stream state. – NathanOliver Feb 25 '16 at 16:17
  • @NathanOlivier : OK, I checked once again, and the `eofbit` is not set indeed. (In my previous attempt, the `eofbit` was set by the `peek` call.) – Leslie N Feb 25 '16 at 16:26
  • @NathanOlivier : after studying and discussing how all this stuff works, I conclude that the behavior of `seekg()` in respect to the `eofbit` is the side-effect of the actual design. I.e. it is a consequence of how it is implemented through `pubseekoff()`, `seekoff()`, etc. It is rather casual than logical. I don't see any real *concept* behind. – Leslie N Feb 29 '16 at 15:24
  • @LeslieN There may not be. One thing I can think of is you seek to the end but then someone add more to the stream so you are no longer at the end. If seeking to the end set the `eofbit` than any read operation would fail. You would be forced to clear the flag and then try to read. By not setting it we put the actual "end of file" check on the next read operation. If we really are at then end the read fails and the flag is set. If more content was added then the read succeeds and we can continue on. – NathanOliver Feb 29 '16 at 15:32
  • @NathanOlivier : I think you tried to give some "posterior" explanation to what is actually happening in respect of the `eofbit`. However if you look at other stream functions, e.g. `peek()`, then you can observe different logic. `peek()` sets the `eofbit` when called at EOF. However `tellg()`, like `seekg()`, does not set it. Why? I don't see any reason... – Leslie N Feb 29 '16 at 15:35
  • @LeslieN Because `peek` actually does a read so it can tell if it is at the end or not. just seeking to the end doesn't mean when you go to read you are still at then end as the stream could be added to. This is why a read operation should be used to actually test if there is nothing left to read and set the appropriate flags. – NathanOliver Feb 29 '16 at 15:40
  • @NathanOlivier : that makes more sense to me. However I could not find this concept written anywhere in the docs... but maybe it is there. – Leslie N Feb 29 '16 at 15:59
  • @NathanOlivier : Moreover this design results in "strange" code for some applications. (Strange for me, at least). Assume you have to implement a function: `bool eofAtNextPos( istream aStream, unsigned int aNext )` It should return if EOF is reached at aNext or less positions ahead. Add the following constraints, too: - the function cannot extract characters from the stream. - you cannot check the size of the stream by calling `seekg( 0, ios::end )` Then you can do it only by "step and peek", i.e. calling `aStream.seekg( 1, ios::cur ); aStream.peek()` and testing the `eofbit` in a loop. – Leslie N Feb 29 '16 at 16:13
  • @NathanOlivier : and such implementation might result in endless loop, if characters are added to the stream meanwhile. – Leslie N Feb 29 '16 at 16:17
  • @NathanOlivier Finally I accept your answer as solution, because nobody else gave any better explanation. Furthermore, I'd appreciate if you could provide me some links to articles, books,etc., that describe the design and behavioral concepts of std streams clearly. – Leslie N Apr 01 '16 at 13:23
  • @LeslieN No problem. You may be intrested in [this](http://www.amazon.com/dp/0201183951/?tag=stackoverfl08-20). I got that from the [C++ book guide](http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list) – NathanOliver Apr 01 '16 at 13:27