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");
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 alternate to open a file for reading is std::ifstream
. Note
that both of these are defined by the fstream
header.
Here’s an example of writing our planet information out to a file:
#include <iostream>
#include <iomanip>
#include <fstream>
#include <vector>
struct Planet
{
std::string name{};
double a{}; // semi-major axis
double e{}; // eccentricity
};
int main() {
const std::vector<Planet> planets {{"Mercury", 0.3871, 0.2056},
{"Venus", 0.7233, 0.0068},
{"Earth", 1.0000, 0.0167},
{"Mars", 1.5237, 0.0934},
{"Jupiter", 5.2029, 0.0484},
{"Saturn", 9.5370, 0.0539},
{"Uranus", 19.189, 0.0473},
{"Neptune", 30.070, 0.0086}};
std::ofstream of("planets.txt");
for (auto p : planets) {
of << std::setw(12) << p.name
<< std::setw(12) << p.a
<< std::setw(12) << p.e << std::endl;
}
of.close();
}
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 <iomanip>
#include <sstream>
#include <fstream>
#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 (auto p : planets) {
std::cout << std::setw(12) << p.name
<< std::setw(12) << p.a
<< std::setw(12) << p.e << std::endl;
}
}
try it…
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);
try it…
Let’s write our own version of wc
. Here’s a text file:
lorem-ipsum.txt
.
Let’s write a C++ code the counts the number of lines, words, and characters from the input file.
We don’t know how to take a command line argument yet, so let’s just hardcode the file name into the program for now.
solution
#include <iostream>
#include <fstream>
int main() {
// open the file
std::ifstream input_file("lorem-ipsum.txt");
if (! input_file.is_open()) {
std::cout << "Error: invalid file" << std::endl;
return 1;
}
// loop over line by line
std::string line{};
unsigned int line_count{0};
unsigned int char_count{0};
unsigned int word_count{0};
bool was_space = false;
while (std::getline(input_file, line)) {
// by design, getline drops the newline, so add it back here
// so we can accurate capture line breaks and get the words right
line += "\n";
for (auto c : line) {
bool test = (c == ' ' || c == '\n');
if (test) {
// if the previous character was a space too, then
// just skip, otherwise, increment the word counter
// and set it as a space
if (!was_space) {
word_count += 1;
was_space = true;
}
} else {
was_space = false;
}
char_count += 1;
}
line_count += 1;
}
std::cout << "number of lines = " << line_count << std::endl;
std::cout << "number of words = " << word_count << std::endl;
std::cout << "number of characters = " << char_count << std::endl;
}