tags:

views:

65

answers:

4

I am reading a text file character by character using ifstream infile.get() in an infinite while loop.

This sits inside an infinite while loop, and should break out of it once the end of file condition is reached. (EOF). The while loop itself sits within a function of type void.

Here is the pseudo-code:

void function (...) {
    while(true) {
        ...
        if ( (ch = infile.get()) == EOF) {return;}
        ...
    }
}

When I "cout" characters on the screen, it goes through all the character and then keeps running outputting what appears as blank space, i.e. it never breaks. I have no idea why. Any ideas?

+1  A: 

try converting the function to an int one and return 1 when reaching EOF

poscaman
+1  A: 

In C++, you don't compare the return value with EOF. Instead, you can use a stream function such as good() to check if more data can be read. Something like this:

while (infile.good()) {
  ch = infile.get();
  // ...
}
casablanca
This is difficult to get to work correctly as well.
Jerry Coffin
Why do you say that? I've always used it and it has never caused me any problems.
casablanca
Because to work correctly, you generally need to test the return value from `infile.get()` and exit in the middle of the loop when it has failed -- and, in fact, the `while (infile.good())` will essentially never be used.
Jerry Coffin
If you only do one `get` per iteration, there should be no problem. If you do call other stream functions, you can simply add checks before/after each call.
casablanca
Actually you can compare the result of get() with EOF. Just not after it has been truncated by assignment to a char. Remember that the output of get() is an integer even though you are retrieving characters (this is to allow special codes to be returned).
Martin York
+1  A: 

One idiom that makes it relatively easy to read from a file and detect the end of the file correctly is to combine the reading and the testing into a single, atomic, event, such as:

while (infile >> ch)

or:

while (std::getline(infile, instring))

Of course, you should also consider using a standard algorithm, such as copy:

std::copy(std::istream_iterator<char>(infile),
          std::istream_iterator<char>(),
          std::ostream_itertror<char>(std::cout, "\n"));

One minor note: by default, reading with >> will skip white space. When you're doing character-by-character input/processing, you usually don't want that. Fortunately, disabling that is pretty easy:

infile.unsetf(std::ios_base::skipws);
Jerry Coffin
Maybe it's just me, but using `std::copy` seems overly complex when all you need is very very simple `while` loop. Good points though.
Whisty
@whisty: but look at the *many* questions that get this wrong, and even the accepted answer to this question, which needs additional work before it'll function correctly at all. Although simple, the while loop is a *constant* source of mistakes!
Jerry Coffin
A: 

The reason it is not working is that get() returns an int but you are using the input as a char.

When you assign the result of get() to a char it is fine as long as the last character read was a character. BUT if the last character read was a special character (such as EOF) then it will get truncated when assigned to a char and thus the subsequent comparison to EOF will always fail.

This should work:

void function (...) 
{
    while(true) 
    {
        ...
        int value;
        if ( (value = infile.get()) == EOF) {return;}
        char ch = value; 
        ...
    }
}

But it should be noted that it is a lot easier to use the more standard pattern where the read is done as part of the condition. Unfortunately the get() does not give you that functionality. So we need to switch to a method that uses iterators.

Note the standard istream_iterator will not work as you expect (as it ignores white space). But you can use the istreambuf_iterator (notice the buf after istream) which does not ignore white space.

void function (...) 
{
    for(std::istreambuf_iterator<char> loop(infile);
        loop != std::istreambuf_iterator<char>();
        ++loop) 
    {
        char ch = *loop; 
        ...
    }
}
Martin York