File I/O#
Reading from / writing to a file uses the same stream operators as we used to take input from a user or write to the screen.
To open a file, we use:
std::ofstream of;
of.open("file.txt");
alternately, we can pass the name of the file to the constructor of the class,
std::ofstream of("file.txt");
Note
We need to #include <fstream> to get access to this function
from the standard library.
Once the file is open, we can send output to it using the stream operator <<:
double x{0.0};
of << x << std::endl;
When we are done with the file, we can close it:
of.close();
Note
C++ will manage closing the file when it goes out of scope (e.g. at the end of a function). However, you can still free it earlier if desired.
The inverse, opening a file for reading, is handled by
std::ifstream. Again, this is defined by the
fstream header.
Writing example#
Here’s an example of writing our planet information out to a file:
#include <fstream>
#include <format>
#include <string>
#include <vector>
struct Planet
{
std::string name;
double a{}; // semi-major axis
double e{}; // eccentricity
};
int main() {
const std::vector<Planet> planets{{.name="Mercury", .a=0.3871, .e=0.2056},
{.name="Venus", .a=0.7233, .e=0.0068},
{.name="Earth", .a=1.0000, .e=0.0167},
{.name="Mars", .a=1.5237, .e=0.0934},
{.name="Jupiter", .a=5.2029, .e=0.0484},
{.name="Saturn", .a=9.5370, .e=0.0539},
{.name="Uranus", .a=19.189, .e=0.0473},
{.name="Neptune", .a=30.070, .e=0.0086}};
std::ofstream of("planets.txt");
for (const auto& p : planets) {
of << std::format("{:12} {:6.3f} {:6.3f}\n",
p.name, p.a, p.e);
}
of.close();
}
Notice that just like sending output to the terminal, we need to
manage line breaks ourselves (we use "\n" here).
Reading our file#
Let’s now look at how to read that file back in.
There are 2 approaches we can take:
Just read in each piece of information as needed, e.g.:
std::ifstream infile{"file.txt"); infile >> x >> y >> z;
Read the file line-by-line into a string and then treat that string as a stream and read from it.
The
getline()function will do this for us:std::string line{}; std::getline(infile, line);
To interpret the string as a stream, we can use
std::stringstream:std::stringstream ss(line); ss >> x >> y >> z;
Here’s an example of reading our file line by line. We rely on the
fact when it reaches the end of the file, getline() will evaluate
as false in a conditional.
#include <iostream>
#include <sstream>
#include <fstream>
#include <format>
#include <string>
#include <vector>
struct Planet
{
std::string name;
double a{}; // semi-major axis
double e{}; // eccentricity
};
int main() {
std::vector<Planet> planets;
std::ifstream data_file("planets.txt");
if (! data_file.is_open()) {
std::cout << "Error opening the file" << std::endl;
return 1;
}
std::string line{};
while (std::getline(data_file, line)) {
Planet p;
std::stringstream ss(line);
ss >> p.name >> p.a >> p.e;
planets.push_back(p);
}
data_file.close();
for (const auto& p : planets) {
std::cout << std::format("{:12} : ({:6.3f}, {:6.3f})\n",
p.name, p.a, p.e);
}
}
Notice that we also check to see if the file is opened successfully. We should have done that for our write example as well.
Caution
A file stream object has an .eof() member that is true when
we reach the end of a file. However, it is only set after a failed
read from the file.
If we try using this in our while loop, it is possible that we
will test as true and then fail to read in the loop body.
Tip
When you open a file for output using ofstream as shown above it overwrites
an existing file of that name.
If we wish instead to append to an existing file, we would do:
std::ofstream of("file.txt", std::ios::app);