I know the title sounds crazy, but I'm experiencing this firsthand right now and I can't think of any reason why this is failing.
I am reading through a file using getline()
At the end of the reading, I call tellg(). However, this call always fails (return value of -1).
Is it a known issue that tellg() doesn't work with getline() or am I doing something else wrong?
The code I am using is very simple, basically
while(getline(file,line))
{
//tokenize and do other things
}
cout<<file.tellg()<<endl;
The file in question is a simple txt file on a regular disk, I tried a file with and without CRLF and it makes no difference.
EDIT: Additional information
gcc/g++ 4.1.2, Linux (RHEL 5)
EDIT2: According to this thread: http://www.cplusplus.com/forum/beginner/3599/#msg15540 It is impossible to use tellg with getline due to some sort of gcc bug. Is this actually the case? (what you read on the internet is not always true =P)
The tellg()
function works by attempting to construct a sentry object and then checking for the failbit
before returning a proper value. If the failbit
is set, it returns -1. Details can be found here or, if you prefer a more official source and don't mind a dry read, the ISO C++ standard ( 27.6.1.3/36a-37
for C++03, 27.7.2.3/39-40
for C++11).
The construction of the sentry first checks any of the error flags (like eofbit
) and, if set, it sets the failbit
and returns. See here for detail (C++03 27.6.1.1.2
, C++11 27.7.2.1.3
),
Hence a tellg()
after the end of file flag has been set will fail. The fact that you're reading lines until getline
returns false means that the stream's eofbit
is being set, hence you've reached the end of the file.
You can see the behavior with this following program:
#include <iostream>
#include <iomanip>
int main (void) {
std::string line;
while (std::getline (std::cin, line)) {
if (line.length() > 20)
line = line.substr(0,17) + "...";
std::cout << "tellg() returned "
<< std::setw(5) << std::cin.tellg()
<< " after " << line << "\n";
}
//std::cin.clear();
std::cout << "tellg() returns: "
<< std::cin.tellg() << '\n';
return 0;
}
When you run that and provide the file itself as input, you see:
tellg() returned 20 after #include <iostream>
tellg() returned 39 after #include <iomanip>
tellg() returned 40 after
tellg() returned 58 after int main (void) {
tellg() returned 80 after std::string l...
tellg() returned 124 after while (std::g...
tellg() returned 156 after if (line....
tellg() returned 202 after line ...
tellg() returned 243 after std::cout...
tellg() returned 291 after << st...
tellg() returned 333 after << " ...
tellg() returned 339 after }
tellg() returned 363 after //std::cin.cl...
tellg() returned 400 after std::cout << ...
tellg() returned 437 after << std::c...
tellg() returned 451 after return 0;
tellg() returned 453 after }
tellg() returned 454 after
tellg() returns: -1
If you uncomment the line in that code which clears the error state variables, it will work:
tellg() returned 20 after #include <iostream>
tellg() returned 39 after #include <iomanip>
tellg() returned 40 after
tellg() returned 58 after int main (void) {
tellg() returned 80 after std::string l...
tellg() returned 124 after while (std::g...
tellg() returned 156 after if (line....
tellg() returned 202 after line ...
tellg() returned 243 after std::cout...
tellg() returned 291 after << st...
tellg() returned 333 after << " ...
tellg() returned 339 after }
tellg() returned 361 after std::cin.clea...
tellg() returned 398 after std::cout << ...
tellg() returned 435 after << std::c...
tellg() returned 449 after return 0;
tellg() returned 451 after }
tellg() returned 452 after
tellg() returns: 452
And, as an aside, it looks like the bug you're referring to may be this one (it's a little unclear since the post you linked to is sadly missing any detail - it would have been better had the poster bothered to support his assertion that it was a known bug by, for example, linking to it).
If that's the case, the first thing you should notice is that it was fixed more than a decade ago so, unless you're using an absolutely ancient gcc
, it's not going to be an issue now.
std::istream::tellg
does not tell you anything if the stream's error flag is set. According to its spec,
Returns: After constructing a sentry object, if
fail() != false
, returnspos_type(-1)
to indicate failure. Otherwise, returnsrdbuf()->pubseekoff(0, cur, in)
.
Referring to std::istream::sentry
, it sets fail
if eof
is already set.
But fail
and eof
are cleared by the clear
function, which is all you need to do.
while(getline(file,line))
{
//tokenize and do other things
}
file.clear(); // reset error state
cout<<file.tellg()<<endl;
And the pubseekoff
function still works even if you don't bother with clear
, so this works too:
cout<< static_cast< std::streamoff >( file.rdbuf()->pubseekoff(0, std::ios::cur, std::ios::in) )
<<endl;
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.