In C++ you can read from and write to a file using streams. A stream is something you can read from or write to; for example, cin is an input stream while cout is an output stream. The good news is that accessing files uses all the same operations you are already familiar with from cin and cout: >>, <<, getline, etc. so you can apply a lot of your existing knowledge to files.

To use the file streams you will have to

#include <fstream>

This gives you access to three new types:

In general, it’s rare that we will want to both read and write the same file at the same time, so it’s usually more reliable to open a file as either an ifstream (if you want to read from it but not change it) or ostream (if you want to change it but don’t care about what’s already there).

Output files

When you open a file for writing (output), the file will be created in the current directory if it did not already exist. To open a file, you have two options:

ofstream f("myfile.txt");

will create a file named myfile.txt in the current directory (i.e., you can see it with ls and even open it in micro) and then attach a stream object named f to it.

Alternatively, you can do

ofstream f;
f.open("myfile.txt");

Here, we create the stream object f, and then open a file with it.

When you open a file for output, you have the option of truncating the file; this means that if the file already exists, it will be complete erased. This is useful because it prevents you from ending up with a file that is a mixture of old and new data. To truncate the file when you open it, do

ofstream f("myfile.txt", fstream::trunc);

To write to the file, just use << as you would with cout:

f << "Hello, world!" << endl; // Write a line of text
f << 12 << 13 << 14;          // Write some ints

float x = 0.34;
f << x;                       // Write from a variable

If you have overloaded << on a class type, then you can use that as well:

class dog { ... };
ostream& operator<< (ostream& out, dog d) { ... }

dog fido{ ... };

f << fido; // Writes fido to the file

When you are done with a file, you should close it:

f.close()

The file will be automatically closed when f goes out of scope, but it’s good practice to close it when you are done with it.

Input files

Opening a file for reading is almost exactly the same, except that you use ifstream instead of ofstream, and the file must already exist. Assuming myfile.txt was generated by the code above:

ifstream f("myfile.txt");

string s;
getline(f, s);    // Reads "Hello, world!" into s

int a, b, c;
f >> a >> b >> c; // Reads 12, 13, 14, into a, b,c

float q;
f >> q;           // Reads 0.34 into q

If you have overloaded >> on a class, you can use that as well:

dog my_dog;

f >> my_dog; // Reads dog details from the file into my_dog

As above, when you are done with a file you should close it:

f.close();

Stream endings, failure, badness, and goodness

If you are reading from a file and you come to the end that is called EOF (end-of-file) and you can check for it with

if(f.eof())
    // We've reached the end of f

EOF is one of several conditions that make a stream fail; a failed stream is one that is broken in some way, and reading/writing from/to it will fail until the stream is reset. You can check to see if a stream is failed with

if(f.fail())
    // f is bad

Conversely, you can check to see if a stream is good with .good:

if(f.good())
    f >> x; // Should work if f is good.

When we write a loop like

while(cin >> x) { ... }

This actually has the same effect as if we wrote

while(cin >> x, x.good()) { ... }

That is, read from the stream and then see if the stream is still good. If the read failed, the stream will be fail and the loop will end.

If a stream is fail you can reset it and see if that makes it work again:

f.clear(); // Reset f to good

Of course, if f is at the end of the file, the next read will just make it fail again, but this can be useful for recovering from other situations. For example, if you don’t know if the next element in the stream is a string or an int, you can try to read the int:

int x;
string s;
f >> x; 

and then check to see if the stream is fail. If it is, the int could not be read and you can clear and try again with the string:

if(f.fail()) {
    f.clear();
    f >> s; 
}   

A failed stream is one that can be reset and might start working again. A bad stream is one that has failed permanently:

if(f.bad()) {
    cout << "Could not open file!" << endl;
    return 1;
}