14

I'm learning File Handling in C++, but there is a problem here. I am trying to read a file. This code is meant to output Hello World. but it outputs 0x22fed8.

#include <iostream>
#include <fstream>

using namespace std;

    int main()
    {
        fstream file;
        file.open("test.txt",ios::in|ios::out);
        file << "Hello World";
        cout << file;
        file.close();

        return 0;
    }

What am I doing wrong?

Robert Harvey
  • 178,213
  • 47
  • 333
  • 501
WoW
  • 923
  • 5
  • 10
  • 15
  • It's not really clear what you're trying to do. The way I read it, you're trying to write "hello world" out to *both* the standard output (the console) *and* to the file test.txt. (which seems pointless) Or maybe you're only trying to write to the file, in which case `cout` shouldn't be there at all. Or maybe you're trying to read data from the file, and print it to the standard output? (in which case the "hello world" string shouldn't be there) What exactly are you trying to do? – jalf Feb 15 '09 at 16:42

4 Answers4

25

Simple solution

As others have pointed out, directly printing a file to a stream does't work. Printing the file contents would require opening another stream that reads from the file, or re-setting your stream's read pointer to the beginning and then reading the whole file again (as others have shown).

C++ doesn't do this automatically but you can do it manually (here, opening a new stream):

ifstream ifs("filename");

Now, writing the file contents to another stream is a trivial addition. Instead of writing the file, simply write the file buffer:

cout << ifs.rdbuf() << endl;

That's all! No loop needed to read the file line by line.

Testing for valid streams

While we're on the subject of loops, beware of code that reads files in a loop in the following manner:

while ( !file.eof() )

This code produces an endless loop when there's a reading error. This an happen in many, many situations. Consider e.g. that the file is deleted while you read it, or that someone removes the USB device containing the file or that the file is wrongly formatted. All these cases would create an infinity loop here. Never only test for eof in a stream.

Luckily, the solution to this problem is also quite simple. Furthermore, it explains why your original code yielded such a weird result. In fact, streams in C++ have an implicit conversion to a bool-like type. For reasons explained elsewhere (cue: safe bool idiom), it is actually converted to void*.

This makes it easy to test whether a stream is in a valid, not-at-end state and can safely be read from. Therefore, we can reformulate the loop appropriately:

while (file) …

The above code relies on the conversion to void* taking place. Any nonnull pointer indicates a valid stream. Now, the same happens in your code:

cout << file;

Since there's no appropriate overload for operator << that takes a stream, C++ looks for other overloads and finds an overload for pointers. So it implicitly calls something like this:

cout << static_cast<void*>(file);

Better solution

I've explained a simple, working solution above. However, this solution requires re-opening the file and reading it to memory again. This doubles the work required. We can make this better by introducing a new class that acts like a stream and that actually sends each output to two streams at once. This way, you can write your data both to the file and to the standard stream at the same time. No need to re-read the file.

The class in itself is quite simple. The following complete code demonstrates the general principle:

#include <iostream>
#include <fstream>

struct sinkpair {
    sinkpair(std::ostream& a, std::ostream& b) : a(a), b(b) { }

    // Forward all ouputs to both streams.
    template <typename T>
    sinkpair& operator <<(T const& value) {
        a << value;
        b << value;
        return *this;
    }

    // Explicit overload needed for manipulators such as `endl`.
    sinkpair& operator <<(std::ostream& (*manip)(std::ostream&)) {
        a << manip;
        b << manip;
        return *this;
    }

private:
    std::ostream& a;
    std::ostream& b;
};

int main() {
    std::ofstream ofs("test.txt");
    sinkpair sp(std::cout, ofs);
    sp << "Hello" << std::endl;
}
Community
  • 1
  • 1
Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • file.rdbuf() may have been flushed out to disk already. Granted, not for an example this trivial. But if he was writing more data... – Mr.Ree Feb 15 '09 at 17:18
  • mree: Wow, good point. I actually misread the code to be *reading* from the file. Now, writing to it is a whole other matter. Basically, the best would then be to make an iostream with two sinks (Boost.Iostreams, anyone?). – Konrad Rudolph Feb 15 '09 at 20:01
  • +1 this is teh answer i think. although there are cases where testing for .eof can be useful (like, in "(stream >> foo >> ws) && stream.eof()"), but it's of course wrong in this situation. – Johannes Schaub - litb Feb 15 '09 at 20:37
  • though, too bad you could have explained what makes "while(file)" work (operator void*()) and in the same sentence explained his output of the strange hex number :) – Johannes Schaub - litb Feb 15 '09 at 20:38
  • Weeell, you finally got your wishes. – Konrad Rudolph Feb 17 '09 at 12:00
  • Let me humbly point to my article from 2011, discussing the difficulty of dealing with the three different error states `badbit`, `failbit`, and `eofbit` while reading from a stream: http://gehrcke.de/2011/06/reading-files-in-c-using-ifstream-dealing-correctly-with-badbit-failbit-eofbit-and-perror/ – Dr. Jan-Philip Gehrcke Jan 18 '15 at 14:25
11

EDIT: With mrree's suggestions.

ifstream fin("file.txt");

if (fin.is_open() == false) {
  // error
}

string line;

while( getline(fin, line) ) {  
  cout << line;
}
Andrei Taranchenko
  • 1,266
  • 1
  • 10
  • 22
7
#include <iostream>
#include <fstream>

int main(int, char **) 
{
  std::ifstream input("test.txt");
  char c;                                                          
  while (input >> c) {
    std::cout << c;
  }
  return 0;
}

Don't include the whole standard namespace. If you want an input file stream, use an ifstream.

You want to output the contents of the file, not the file.

If you want to write to a file, then read it back and send it to stdout,

#include <iostream>
#include <fstream>

int main(int, char **)
{
  std::fstream file("test.txt",std::ios::in|std::ios::out);
  file << "Hello" << std::endl;
  file.seekp(std::ios::beg);
  char c;
  while (file >>c) {
      std::cout << c ;
  }
  return 0;
}

Konrad has the best answer, but consider the high-level approach:

#include <algorithm>
#include <iostream>
#include <fstream>
#include <iterator>

int main(int, char **)
{
  std::fstream file("test.txt",std::ios::in|std::ios::out);
  file << "Hello" << std::endl;
  file.seekp(std::ios::beg);
  std::copy(
      std::istream_iterator<char>(file),
      std::istream_iterator<char>(), 
      std::ostream_iterator<char>(std::cout)
      );
  return 0;
}
Thomas L Holaday
  • 13,614
  • 6
  • 40
  • 51
6

Lets examine the line

cout << file;

The reason this outputs a number is because deep under the hood fstream is a file pointer. By passing file to cout you're essentially asking for a cout of an fstream. This will default to the value of the underlying handle for the file.

If you want to output the contents of the file, you'll need to read it in and output it line by line.

fstream file;
file.open("test.txt",ios::in|ios::out);
file << "Hello World";
file.seekg (0, ios::beg);
while ( !file.eof() ) {
  string temp;
  file >> temp;
  cout << temp << std::eol;
}

file.close();
Bill the Lizard
  • 398,270
  • 210
  • 566
  • 880
JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
  • 1
    You know that `while(!file.eof())` loops are *always* broken, right? `failbit` and `badbit` must be tested was well!!! – Konrad Rudolph Feb 15 '09 at 16:19
  • Actually I didn't :( . My knowledge of iostream is minimal at best. I haven't done meaningful work with it since ... probably sophmoore year of college. – JaredPar Feb 15 '09 at 16:30
  • Instead of looping you can just cout << file.rdbuf() – Logan Capaldo Feb 15 '09 at 16:50
  • cout << file invokes operator void *(), which is set to NULL if anything bad has happened. See: http://www.cplusplus.com/reference/iostream/ios/operator_voidpt.html – Mr.Ree Feb 15 '09 at 17:14
  • file.rdbuf() may have been flushed out to disk already. Granted, not for an example this trivial. But if he was writing more data... – Mr.Ree Feb 15 '09 at 17:17