File Handling in C++
Hey! Welcome back. Today, we’re going to learn how to make our program’s memory immortal.
Up until now, every variable you’ve created has lived on the Stack or the Heap. That means the moment your program exits or the computer turns off, all your data vanishes. To save data permanently, we need to write it to the Hard Disk inside files. In C++, we do this using File Handling.
Understanding Streams in C++
Before we touch files, we need to understand how C++ handles input and output. C++ uses the concept of Streams.
Think of a stream like a water pipe or a conveyor belt. Instead of transporting water or boxes, it transports a continuous flow of bytes (characters) from a source (like your keyboard) to a destination (like your terminal screen or a file on your hard drive).
graph TD
ios[Virtual Base Class: ios <br/> Manages formatting states & flags]
ios --> istream[Input Stream: istream <br/> e.g., cin]
ios --> ostream[Output Stream: ostream <br/> e.g., cout, cerr, clog]
istream --> iostream[Input/Output Stream: iostream]
ostream --> iostream
istream --> ifstream[File Input: ifstream <br/> Reads from disk]
ostream --> ofstream[File Output: ofstream <br/> Writes to disk]
iostream --> fstream[File Input/Output: fstream <br/> Reads & writes disk]
To use console-based streams, you include <iostream>. To use file-based streams, you include <fstream>.
Standard Console Streams
C++ gives you four pre-defined stream objects for console input/output:
std::cin: Connects to the standard input (usually keyboard).std::cout: Connects to the standard output (usually the terminal monitor). It is buffered (stores data in memory and flushes it in batches to be fast).std::cerr: Connects to the standard error. It is unbuffered (prints messages immediately, ensuring error alerts show up even if the program crashes a millisecond later).std::clog: Connects to the standard logging. It is buffered, ideal for writing high-volume logging reports without slowing down execution.
graph TD
subgraph Buffered Output
cout_stream[std::cout / std::clog] --> buf[Memory Buffer] -->|Flushed when full or by std::endl| screen1[Console Terminal]
end
subgraph Unbuffered Output
cerr_stream[std::cerr] -->|Bypasses buffer immediately| screen2[Console Terminal]
end
Let’s see standard console streams in action:
Example:
#include <iostream>
#include <string>
int main() {
std::string name;
// 1. std::cout (Buffered)
std::cout << "Enter your nickname: ";
// 2. std::cin
std::cin >> name;
std::cout << "Hey, " << name << "! Welcome to C++ Mastery." << std::endl;
// 3. std::cerr (Unbuffered Error)
std::cerr << "[ERROR] This is an immediate error alert!" << std::endl;
// 4. std::clog (Buffered Log)
std::clog << "[LOG] User entered name successfully." << std::endl;
return 0;
}
Output:
Enter your nickname: striverA2Z
Hey, striverA2Z! Welcome to C++ Mastery.
[ERROR] This is an immediate error alert!
[LOG] User entered name successfully.
File Stream Operations: Disk I/O
To read or write files, we use three classes from the <fstream> library:
std::ofstream: Output File Stream. Used to create/write files (data flows from program variables to the hard disk).std::ifstream: Input File Stream. Used to read files (data flows from the hard disk into program variables).std::fstream: Input/Output File Stream. Can do both.
graph LR
subgraph Data Flow
direction LR
vars[Program Variables <br/> RAM] -->|ofstream / Operator <<| disk[File on Hard Disk]
disk -->|ifstream / Operator >> or getline| vars_read[Program Variables <br/> RAM]
end
1. Creating and Writing to a File
When you open a file using ofstream, C++ will automatically create the file if it doesn’t exist. If the file does exist, C++ will overwrite its contents by default.
Example:
#include <iostream>
#include <fstream> // Required for file streams
int main() {
// Open/Create a file named "tutorial.txt"
std::ofstream outFile("tutorial.txt");
// Always check if the file opened successfully
if (!outFile) {
std::cerr << "Error creating file!" << std::endl;
return 1;
}
// Write lines of text using the stream insertion operator <<
outFile << "Line 1: Writing C++ code is fun!" << std::endl;
outFile << "Line 2: Pointers aren't that scary." << std::endl;
// Close the file to free system resources
outFile.close();
std::cout << "File written successfully." << std::endl;
return 0;
}
Output:
File written successfully.
2. Reading from a File
To read a file, we open it with ifstream. We can read it:
- Word-by-word: Using
inFile >> variable. (Stops at spaces). - Line-by-line: Using
std::getline(inFile, line). (Stops at newlines).
Example:
#include <iostream>
#include <fstream>
#include <string>
int main() {
std::ifstream inFile("tutorial.txt");
if (!inFile.is_open()) {
std::cerr << "Error opening file for reading!" << std::endl;
return 1;
}
std::string line;
std::cout << "Reading file line-by-line:" << std::endl;
// Read line-by-line until end-of-file (EOF) is reached
while (std::getline(inFile, line)) {
std::cout << "-> " << line << std::endl;
}
inFile.close(); // Close connection
return 0;
}
Output:
Reading file line-by-line:
-> Line 1: Writing C++ code is fun!
-> Line 2: Pointers aren't that scary.
3. Appending Content to a File
If you don’t want to wipe out your existing file, you open it in append mode by passing std::ios::app as a second parameter to the ofstream constructor.
Example:
#include <iostream>
#include <fstream>
#include <string>
int main() {
// Open in append mode
std::ofstream outFile("tutorial.txt", std::ios::app);
if (!outFile) {
std::cerr << "Error opening file!" << std::endl;
return 1;
}
// Write additional content
outFile << "Line 3: Appended this line later!" << std::endl;
outFile.close();
// Let's read it back to confirm
std::ifstream inFile("tutorial.txt");
std::string line;
std::cout << "Updated file contents:" << std::endl;
while (std::getline(inFile, line)) {
std::cout << line << std::endl;
}
inFile.close();
return 0;
}
Output:
Updated file contents:
Line 1: Writing C++ code is fun!
Line 2: Pointers aren't that scary.
Line 3: Appended this line later!
4. Closing Files: Why it Matters
You should always call .close() when you’re done with a file.
- Flushing data: File writes are held in a memory buffer. Closing the file forces C++ to flush any remaining data to the physical disk.
- Release handles: The operating system locks files when a program opens them. If you keep opening files without closing them, you will leak “file descriptors,” and eventually, your program won’t be allowed to open any more files.
[!TIP] C++ file stream destructors automatically call
.close()when the stream object goes out of scope. However, explicitly calling.close()is best practice, as it lets you control when files unlock and allows you to check for write failures immediately.
File Handling Summary
| Stream Type | Mode Flag | Operation | Behavior |
|---|---|---|---|
std::ofstream | None (Default) | Write | Creates new file or overwrites existing file. |
std::ofstream | std::ios::app | Append | Keeps existing content, appends new text to the end. |
std::ifstream | None (Default) | Read | Reads existing file. Fails if file does not exist. |
std::fstream | std::ios::in | std::ios::out | Read/Write | Allows bidirectional reading and writing on the same file. |
Level Up: Extra Practice & Interview Questions
Q1. Write a C++ program to create a file named “striverA2Z.txt”, write the word “Hey” to it, and read it back.
This tests standard file creation, writing, and character retrieval.
Code:
#include <iostream>
#include <fstream>
#include <string>
int main() {
// 1. Create and write
std::ofstream outFile("striverA2Z.txt");
if (outFile.is_open()) {
outFile << "Hey" << std::endl;
outFile.close();
}
// 2. Read back
std::ifstream inFile("striverA2Z.txt");
if (inFile.is_open()) {
std::string word;
inFile >> word;
std::cout << "File Content: " << word << std::endl;
inFile.close();
}
return 0;
}
Output:
File Content: Hey
Q2. Append the word “striverA2Z” to “striverA2Z.txt” and print the full file.
This practices the std::ios::app append modifier.
Code:
#include <iostream>
#include <fstream>
#include <string>
int main() {
// Append
std::ofstream outFile("striverA2Z.txt", std::ios::app);
if (outFile.is_open()) {
outFile << "striverA2Z" << std::endl;
outFile.close();
}
// Print contents
std::ifstream inFile("striverA2Z.txt");
if (inFile.is_open()) {
std::string line;
std::cout << "Contents:" << std::endl;
while (std::getline(inFile, line)) {
std::cout << line << std::endl;
}
inFile.close();
}
return 0;
}
Output:
Contents:
Hey
striverA2Z
Q3. In the updated file, count the total number of words and print the result.
Reading word-by-word with >> is perfect here since it automatically stops at whitespace and increments our count.
Code:
#include <iostream>
#include <fstream>
#include <string>
int main() {
std::ifstream inFile("striverA2Z.txt");
if (!inFile) {
std::cerr << "File not found!" << std::endl;
return 1;
}
std::string word;
int wordCount = 0;
// inFile >> word evaluates to false when there are no more words to read
while (inFile >> word) {
wordCount++;
}
std::cout << "Total Words: " << wordCount << std::endl;
inFile.close();
return 0;
}
Output:
Total Words: 2
quiz Test Your Understanding
Which stream class is used for file reading operations in C++?
The ifstream class (input file stream) is specifically designed to read data from local files.