1

I have this code

#include <iostream>
#include <fstream>

using namespace std;

int main(int argc, char **argv) {
    ifstream in;
    in.open(*(argv+1));
    char c;

    while (true) {
        if (in.eof())
            break;
        c = in.get();
        cout << c;
    }
    in.close();

    return 0;
}

I read the file which is passed via the command line as the first argument. While reading the file, I get the char '?' as the last one. Why?

If I remove this line

in.open(*(argv+1));

I will get only the char '?'.

  • 2
    do not use `eof()` to control reading from a file. Use `while(c = in.get())`. read [this](http://stackoverflow.com/questions/5431941/why-is-while-feof-file-always-wrong) – NathanOliver Apr 08 '15 at 16:46
  • @NathanOliver That's not very good either, just use the stream (e.g. `while (in)`) – Some programmer dude Apr 08 '15 at 16:47
  • To extend on @NathanOliver's comment: http://stackoverflow.com/questions/5605125/why-is-iostreameof-inside-a-loop-condition-considered-wrong – πάντα ῥεῖ Apr 08 '15 at 16:47
  • A question to the OP, why are you using the `*(argv+1)` syntax? It's valid but a little harder to understand than the normal `argv[1]` syntax. – Some programmer dude Apr 08 '15 at 16:49
  • @JoachimPileborg Why would you do that? You want the read operation to control the loop so the first time the read fails the loop stops. – NathanOliver Apr 08 '15 at 16:49
  • 1
    @NathanOliver The problem is that the used overload of the [`get`](http://en.cppreference.com/w/cpp/io/basic_istream/get) function might not return zero on error or end of file, it returns `Traits::eof()` (which defaults to [`std::char_traits::eof`](http://en.cppreference.com/w/cpp/string/char_traits/eof)). There's nothing to say that the value will be zero. – Some programmer dude Apr 08 '15 at 16:51
  • 1
    @JoachimPileborg good point, but `while(in)` will still read past the end of file, doesn't it? Shouldn't you test the state of the stream after each `get`? – vsoftco Apr 08 '15 at 16:58
  • 1
    @vsoftco That's true, so better use the `>>` operator as in your answer. :) – Some programmer dude Apr 08 '15 at 16:59

2 Answers2

7

In the while loop you end up reading eof:

c = in.get(); // reads eof
cout << c;

This is not the best way to read a text file, use

while(in >> c){ // process c here }

or

while(in >> std::noskipws >> c) { // process c here } 

if you want the white spaces (like \n) included.

If your file is in binary, then it doesn't make too much sense to display it, but if you want, then use get() but test if(!in) break; before further processing

c = in.get();
if (!in) // test the stream
    break;
cout << c; // then process

If you just want to display the file, the easiest way is to use the stream buffer directly, like

std::cout << in.rdbuf(); // display the whole file at once

Related: Why is iostream::eof inside a loop condition considered wrong?

Community
  • 1
  • 1
vsoftco
  • 52,188
  • 7
  • 109
  • 221
  • I don't read eof, cause I break the loop. if (in.eof()) break; –  Apr 16 '15 at 15:06
  • @MasterOfLife say you are at the end of the file (immediately before EOF). Then you test `if(in.eof())`, which will be false (you didn't yet reach EOF). Next you read `c = in.get()`, now you get the EOF in `c`. You should test for EOF **after** you read, and if the stream is EOF then discard the read (and break from the loop). In your case, change the order of `if` and `c = in.get()`, that is, put `c = in.get()` at the top of the loop. I highly recommend you to carefully read the link I posted. – vsoftco Apr 16 '15 at 15:14
1

The istream::get() method returns an untranslated value from the input stream.

If the value is not printable, your console may translate the character to '?'.

A better method is to print the value of the character along with the character:

cout << "\'" << c << "\', 0x" << hex << ((unsigned int) c) << endl;

This will allow you to see unprintable characters.

Thomas Matthews
  • 52,985
  • 12
  • 85
  • 144