Data generated during program execution typically resides in volatile memory and is lost when the application terminates. To preserve this data, programmers must implement file handling mechanisms to persist information to the hard drive. In C++, this functionality is provided by the standard library header <fstream>.
Files are generaly categorized into two types based on their storage format:
- Text Files: Data is stored as human-readable ASCII or Unicode characters.
- Binary Files: Data is stored in its raw memory format (binary), which is efficient for storage but not human-readable without a specific viewer.
C++ provides three primary classes to handle file streams:
ofstream: Dedicated to writing (output) data to files.ifstream: Dedicated to reading (input) data from files.fstream: Supports both reading and writing operations.
Text File Operations
Writing to Text Files
The process of writing to a text file involves a standard sequence of steps: including the header, creating a stream object, opening the file, writing data, and closing the stream.
| Open Mode Flag | Descripsion |
|---|---|
ios::in |
Opens the file for reading. |
ios::out |
Opens the file for writing. |
ios::ate |
Initializes the file pointer to the end of the file. |
ios::app |
Appends new data to the end of the file without truncating existing content. |
ios::trunc |
If the file exists, its contents are discarded before opening. |
ios::binary |
Opens the file in binary mode. |
These flags can be combined using the bitwise OR operator (|). For example, to open a file for writing in binary mode: ios::binary | ios::out.
#include <iostream>
#include <fstream>
#include <string>
void create_log_file() {
std::ofstream output_stream;
// Open file; if it doesn't exist, it will be created.
output_stream.open("system_log.txt", std::ios::out);
if (!output_stream.is_open()) {
std::cerr << "Error: Unable to open file for writing." << std::endl;
return;
}
output_stream << "Event ID: 404" << std::endl;
output_stream << "Severity: High" << std::endl;
output_stream << "Message: Connection timeout detected." << std::endl;
output_stream.close();
}
int main() {
create_log_file();
return 0;
}
Reading from Text Files
Reading data follows a similar pattern. The stream is opened, and data is extracted line-by-line or character-by-character. It is crucial to verify that the file opened successfully before attempting to read.
#include <iostream>
#include <fstream>
#include <string>
void read_log_file() {
std::ifstream input_stream;
input_stream.open("system_log.txt", std::ios::in);
if (!input_stream.is_open()) {
std::cerr << "Error: File not found." << std::endl;
return;
}
std::cout << "--- Method 1: Stream Extraction (Whitespace delimited) ---" << std::endl;
std::string word;
while (input_stream >> word) {
std::cout << word << std::endl;
}
// Reset the stream to the beginning for the next demo
input_stream.clear();
input_stream.seekg(0, std::ios::beg);
std::cout << "\n--- Method 2: getline() (Line by Line) ---" << std::endl;
std::string line;
while (std::getline(input_stream, line)) {
std::cout << line << std::endl;
}
// Reset again
input_stream.clear();
input_stream.seekg(0, std::ios::beg);
std::cout << "\n--- Method 3: Character-by-Character ---" << std::endl;
char character;
while (input_stream.get(character)) {
std::cout << character;
}
input_stream.close();
}
Binary File Operations
Binary file operations allow you to write and read raw memory blocks. This is particularly useful for saving complex data structures or class objects directly to disk. The open mode must include ios::binary.
Writing Binary Data
To write binary data, use the write() member function. Its prototype is ostream& write(const char* buffer, int len). You must cast the address of your data object to a char* pointer.
#include <iostream>
#include <fstream>
#include <cstring>
class UserAccount {
public:
UserAccount() {}
UserAccount(const char* u, int i, double b) {
strncpy(username, u, 64);
userId = i;
balance = b;
}
char username[64];
int userId;
double balance;
};
void save_account_data() {
UserAccount account = {"john_doe", 55123, 1250.75};
std::ofstream bin_file;
bin_file.open("account_data.bin", std::ios::out | std::ios::binary);
if (bin_file.is_open()) {
// Serialize the object to binary
bin_file.write(reinterpret_cast<char*>(&account), sizeof(account));
bin_file.close();
}
}
Reading Binary Data
Reading binary data uses the read() member functon: istream& read(char* buffer, int len). The data is read directly from the file into the memory address of the target object.
#include <iostream>
#include <fstream>
void load_account_data() {
UserAccount loaded_account;
std::ifstream bin_file;
bin_file.open("account_data.bin", std::ios::in | std::ios::binary);
if (!bin_file.is_open()) {
return;
}
// Read raw bytes directly into the object structure
bin_file.read(reinterpret_cast<char*>(&loaded_account), sizeof(loaded_account));
std::cout << "User: " << loaded_account.username << std::endl;
std::cout << "ID: " << loaded_account.userId << std::endl;
std::cout << "Balance: " << loaded_account.balance << std::endl;
bin_file.close();
}