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:
ifstream
– A stream for reading (input) from files.ofstream
– A stream for writing (output) to files.fstream
– A stream for reading and writing from/to files.
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 fail
ed 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;
}