Eof() Bad Practice

eof() bad practice?

You can use eof to test for the exact condition it reports - whether you have attempted to read past end of file. You cannot use it to test whether there's more input to read, or whether reading succeeded, which are more common tests.

Wrong:

while (!cin.eof()) {
cin >> foo;
}

Correct:

if (!(cin >> foo)) {
if (cin.eof()) {
cout << "read failed due to EOF\n";
} else {
cout << "read failed due to something other than EOF\n";
}
}

Why is iostream::eof inside a loop condition (i.e. `while (!stream.eof())`) considered wrong?

Because iostream::eof will only return true after reading the end of the stream. It does not indicate, that the next read will be the end of the stream.

Consider this (and assume then next read will be at the end of the stream):

while(!inStream.eof()){
int data;
// yay, not end of stream yet, now read ...
inStream >> data;
// oh crap, now we read the end and *only* now the eof bit will be set (as well as the fail bit)
// do stuff with (now uninitialized) data
}

Against this:

int data;
while(inStream >> data){
// when we land here, we can be sure that the read was successful.
// if it wasn't, the returned stream from operator>> would be converted to false
// and the loop wouldn't even be entered
// do stuff with correctly initialized data (hopefully)
}

And on your second question: Because

if(scanf("...",...)!=EOF)

is the same as

if(!(inStream >> data).eof())

and not the same as

if(!inStream.eof())
inFile >> data

Why is eof() never returning true?

The problem with your use of std::istream::get is that it doesn't consume the delimiter. It will work fine for the first call, but then the next call will immediately see the newline left over from the previous call, and not read anything.

If you want to read lines, either use std::istream::getline (if you persist on using character arrays) or std::getline with std::string which is what I recommend.


You also don't need explicit eof checks, instead rely on the fact that all functions returns (a reference to) the stream and that the streams can be used directly in conditions, so do e.g.

if (!std::getline(...))
// end of file or error

why does eof() not work as I expect?

The problem is that when extracting symbols for 45,it tries to extract symbol after '5' (to check if number continues further) and sees end of file, setting eofbit. This makes in.good() test to fail. Suggestions:

while (!in.eof()){
in >> i;
if ( in ) { //Not .good(), just conversion to bool

Or

while (!in.eof()){
if ( in >> i; ) { //both extraction and checking in same operation

Remember, .good() is not the same as checking stream state. .good() is telling if stream ready for further input. Bool conversion does ! .fail() and checks if last operation was executed succesfully

End of File (EOF)

One way is to check wether the first input operation succeeds or not:

#include <iostream>

int main() {
double number;

if(std::cin >> number)
{
while (std::cin >> number);

std::cout << number;
}
else
{
std::cout << "There is no number :(";
}
}

Few things I changed here:

  • removed using namespace std;, it's considered bad parctice
  • a while is better suited for this task than a half-empty for

And of course this can be generalized to any std::basic_istream.

Why is failbit set when I enter EOF?

eofbit is set when a read operation encounters EOF while reading data into the stream's buffer. The data hasn't been processed yet.

failbit is set when the requested data fails to be extracted from the buffer, such as when reading an integer with operator>>. While waiting for digits to arrive, EOF could occur. eofbit alone is not enough to enter an error state, as there may be usable data in the buffer.

So, for example, imagine a while (cin >> num) loop is used and the user enters 123<Ctrl-Z>.

  • on the 1st iteration, operator>> reads 1, 2, 3 into the buffer, then encounters Ctrl-Z, so it sets eofbit and stops reading. 123 is then extracted from the buffer into num and the operator exits. At this point, the stream is not yet in an error state. When the stream's bool conversion is evaluated by while, it returns true, allowing the while body to be entered so it can process num.

  • on the next iteration, operator>> sees eofbit is set, preventing further reading. There is nothing left in the buffer to extract into num, so the operator sets failbit and exits. The stream is now in an error state. When the stream's bool conversion is evaluated by while, it returns false, breaking the while loop.

using eof on C++

You're pretty close, but probably being influenced a bit more by your Pascal background than is ideal. What you probably want is more like:

#include<iostream>
using namespace std; // Bad idea, but I'll leave it for now.

int main()
{
int k,sum = 0; // sum needs to be initialized.
while (cin >> k)
{
sum += k; // `sum = sum + k;`, is legal but quite foreign to C or C++.
}
cout << sum<<endl;
}

Alternatively, C++ can treat a file roughly like a sequential container, and work with it about like it would any other container:

int main() { 
int sum = std::accumulate(std::istream_iterator<int>(std::cin),
std::istream_iterator<int>(),
0); // starting value
std::cout << sum << "\n";
return 0;
}

Why does std::fstream set the EOF bit the way it does?

Because this way it can detect EOF without knowing how large the file is. All it has to do is simply attempt to read and if the read is short (but not an error), then you have reached the end of the file.

This mirrors the functionality of the read system call, which file IO typically ends up calling (win32 stuff may call ReadFile but I believe that the functionality is similar).

From the read manpage "RETURN VALUE" section (emphasis added):

On success, the number of bytes read
is returned (zero indicates end of
file)
, and the file position is
advanced by this number. It is not an
error if this number is smaller than
the number of bytes requested; this
may happen for example because fewer
bytes are actually available right now
(maybe because we were close to
end-of-file, or because we are reading
from a pipe, or from a terminal), or
because read() was interrupted by a
signal. On error, -1 is returned, and
errno is set appropriately. In this
case it is left unspecified whether
the file position (if any) changes.

BTW: a good way to write what you wanted would be like this:

T something;
while(file.read(something, sizeof(something))) {
// process your 'something'
}

this works because file.read (like many members of iostream) return a reference to the iostream itself. All of which have an operator overloaded to allow testing the stream state. Similarly to read from std::cin, while(std::cin >> x) { ... } works as well.

EDIT: you should know that testing vs. fail can be equally wrong for the same reason. From the page you linked to fail() returns if the previous operation failed. Which means you need to perform a read or other relevant operation before testing it.



Related Topics



Leave a reply



Submit