Skip to content

C++ Files and Streams

So far, we have used the <iostream> standard library, which provides std::cin and std::cout for reading from standard input and writing to standard output. To work with files, C++ provides the <fstream> library.

The <fstream> library defines three main data types for file I/O:

Data TypeDescription
std::ofstreamOutput File Stream: Used to create files and write data to files.
std::ifstreamInput File Stream: Used to read data from files.
std::fstreamFile Stream: Combines ofstream and ifstream capabilities for both reading and writing.

To perform file processing in C++, you must include the <fstream> header file. It’s also common to include <iostream> for console interaction and <string> for handling text.

In modern C++, the easiest way to open a file is during the stream object’s construction. This uses the RAII (Resource Acquisition Is Initialization) principle, meaning the file is automatically closed when the stream object goes out of scope (e.g., at the end of a function).

std::ofstream outputFile("mydata.txt"); // Opens for writing (truncates if exists)
std::ifstream inputFile("mydata.txt"); // Opens for reading
std::fstream ioFile("mydata.txt", std::ios::in | std::ios::out); // Opens for read/write

You should always check if the file was opened successfully:

std::ofstream outputFile("mydata.txt");
if (!outputFile) { // Or !outputFile.is_open()
std::cerr << "Error opening file for writing!" << std::endl;
// Handle error, maybe return or exit
}

Alternatively, you can create the stream object first and then call the open() member function. The second argument specifies the mode:

void open(const std::string& filename, std::ios_base::openmode mode = std::ios_base::out); // ofstream default
void open(const std::string& filename, std::ios_base::openmode mode = std::ios_base::in); // ifstream default

Common file modes (defined in std::ios_base):

Mode FlagDescription
std::ios::appAppend to the end of the file.
std::ios::ateSeek to the end of the file upon opening.
std::ios::binaryOpen in binary mode (rather than text mode).
std::ios::inOpen for reading (default for ifstream).
std::ios::outOpen for writing (default for ofstream).
std::ios::truncTruncate file to zero length if it exists (default for ofstream when not using app or in).

Combine modes using the bitwise OR operator (|). For example, to open for output in binary mode, appending if the file exists:

std::ofstream outfile;
outfile.open("data.bin", std::ios::out | std::ios::binary | std::ios::app);

If you use the RAII approach (opening in the constructor), the file is closed automatically when the stream object is destroyed. This is the preferred method as it’s less error-prone.

If you used the open() method, you can explicitly close the file using close(). However, relying on RAII is generally better.

// Only needed if RAII wasn't used or if you need to check close status
if (outfile.is_open()) {
outfile.close();
}

Use the stream insertion operator (<<) with an ofstream or fstream object, just like you use std::cout:

std::ofstream outputFile("greeting.txt");
if (outputFile) {
std::string name = "Alice";
int age = 30;
outputFile << "Hello, " << name << "!\n";
outputFile << "Your age is: " << age << std::endl;
} // File automatically closed here

Use the stream extraction operator (>>) with an ifstream or fstream object, similar to std::cin. To read line by line, use std::getline().

// Reading whitespace-separated values
std::ifstream inputFile("data.txt");
int value1;
double value2;
std::string word;
if (inputFile) {
inputFile >> value1 >> value2 >> word;
// Check if reads were successful: if (!inputFile) { handle error... }
}
// Reading line by line
std::ifstream textFile("document.txt");
std::string line;
if (textFile) {
while (std::getline(textFile, line)) { // Reads until newline or EOF
std::cout << "Read line: " << line << std::endl;
}
} // File automatically closed here

Important Note: Mixing >> and std::getline() requires care, as >> often leaves the newline character in the stream, which std::getline() might then read as an empty line. You might need inputFile.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); after using >> and before std::getline().

This program asks the user for their name and age, writes them to user_data.txt, then reads the data back and displays it.

#include <fstream>
#include <iostream>
#include <string>
#include <limits> // Required for numeric_limits
int main() {
std::string name;
int age;
const std::string filename = "user_data.txt";
// --- Writing to the file ---
{
std::ofstream outfile(filename); // Opens for writing (RAII style)
if (!outfile) {
std::cerr << "Error opening " << filename << " for writing." << std::endl;
return 1;
}
std::cout << "Writing to the file " << filename << std::endl;
std::cout << "Enter your name: ";
std::getline(std::cin, name); // Read full name with spaces
std::cout << "Enter your age: ";
std::cin >> age;
// Ignore the newline character left by std::cin >> age
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
// Write data to the file
outfile << name << std::endl; // Write name on its own line
outfile << age << std::endl; // Write age on its own line
std::cout << "Data written successfully." << std::endl;
// outfile is automatically closed when it goes out of scope here
}
std::cout << "\nReading from the file " << filename << std::endl;
// --- Reading from the file ---
{
std::ifstream infile(filename); // Opens for reading (RAII style)
if (!infile) {
std::cerr << "Error opening " << filename << " for reading." << std::endl;
return 1;
}
std::string read_name;
int read_age;
// Read data from the file
if (std::getline(infile, read_name) && (infile >> read_age)) {
// Display the data
std::cout << "Name read: " << read_name << std::endl;
std::cout << "Age read: " << read_age << std::endl;
} else {
std::cerr << "Error reading data from the file." << std::endl;
}
// infile is automatically closed when it goes out of scope here
}
return 0;
}

Sample interaction:

Writing to the file user_data.txt
Enter your name: Ada Lovelace
Enter your age: 36
Data written successfully.
Reading from the file user_data.txt
Name read: Ada Lovelace
Age read: 36

Both input (istream) and output (ostream) streams maintain a position indicator specifying the next read/write location. You can manipulate these using seekg (seek get) for input streams and seekp (seek put) for output streams.

The functions typically take an offset (usually std::streamoff, a type like long long) and an optional direction (std::ios_base::seekdir):

  • std::ios::beg: Offset relative to the beginning of the stream (default).
  • std::ios::cur: Offset relative to the current position.
  • std::ios::end: Offset relative to the end of the stream.

Example usage for an input stream fileObject:

// Position to the 10th byte from the beginning
fileObject.seekg(10, std::ios::beg); // or fileObject.seekg(10);
// Move 5 bytes forward from the current position
fileObject.seekg(5, std::ios::cur);
// Position 8 bytes back from the end
fileObject.seekg(-8, std::ios::end);
// Position at the end of the file
fileObject.seekg(0, std::ios::end);
// Get current position
std::streampos currentPos = fileObject.tellg();

seekp and tellp are the corresponding functions for output streams (ostream). Be cautious when using seeking in text mode, as the interpretation of offsets might be system-dependent due to newline character translations. Seeking is more reliable in binary mode (std::ios::binary).

For more advanced file system operations like creating directories, checking file existence, or iterating through directory contents, consider using the C++17 <filesystem> library.