File handling is an essential part of any programming language as it allows you to store and retrieve data from external files. C++ provides a comprehensive set of classes and functions in the <fstream> header for file input and output operations.
Introduction to File Handling
File handling in C++ enables you to:
- Store program output in a file
- Read data from existing files
- Process data in files
- Create data records
- Update information in files
C++ uses streams for file operations, similar to how cout and cin work for console input/output.
File Stream Classes
C++ provides three main classes for file operations:
- ifstream (Input File Stream): Used for reading from files
- ofstream (Output File Stream): Used for writing to files
- fstream (File Stream): Used for both reading from and writing to files
These classes are derived from ios base class through istream and ostream classes.
Opening and Closing a File
Opening a File
To perform operations on a file, you first need to open it. You can open a file in two ways:
Method 1: Using the constructor
#include <fstream>
#include <iostream>
using namespace std;
int main() {
// Opening a file for reading
ifstream inputFile("data.txt");
// Opening a file for writing
ofstream outputFile("output.txt");
// Opening a file for both reading and writing
fstream dataFile("records.txt", ios::in | ios::out);
// Check if files were opened successfully
if (!inputFile) {
cout << "Error opening input file!" << endl;
return 1;
}
if (!outputFile) {
cout << "Error opening output file!" << endl;
return 1;
}
if (!dataFile) {
cout << "Error opening data file!" << endl;
return 1;
}
// File operations will go here...
return 0;
}
Method 2: Using the open() method
#include <fstream>
#include <iostream>
using namespace std;
int main() {
ifstream inputFile;
ofstream outputFile;
fstream dataFile;
// Opening files
inputFile.open("data.txt");
outputFile.open("output.txt");
dataFile.open("records.txt", ios::in | ios::out);
// Check if files were opened successfully
if (!inputFile.is_open()) {
cout << "Error opening input file!" << endl;
return 1;
}
// File operations will go here...
return 0;
}
Closing a File
Once you’re done with a file, you should close it to release the system resources. You can close a file using the close() method:
inputFile.close();
outputFile.close();
dataFile.close();
File Opening Modes
When opening a file, you can specify the mode that determines how the file should be opened. File modes are defined in the ios class:
| Mode | Description |
|---|---|
ios::in | Open for input operations (reading) |
ios::out | Open for output operations (writing) |
ios::app | Append to the end of the file |
ios::ate | Set the initial position at the end of the file |
ios::trunc | If the file exists, its contents will be truncated before opening |
ios::binary | Open in binary mode (instead of text mode) |
You can combine these modes using the bitwise OR operator (|):
// Open a file for both reading and writing in binary mode
fstream file("data.dat", ios::in | ios::out | ios::binary);
// Open a file for appending
ofstream logFile("log.txt", ios::app);
// Open a file for writing and truncate it if it exists
ofstream newFile("newdata.txt", ios::out | ios::trunc);
Reading from a File
There are several ways to read data from a file in C++:
1. Using extraction operator (>>)
#include <fstream>
#include <iostream>
#include <string>
using namespace std;
int main() {
ifstream inputFile("data.txt");
if (!inputFile) {
cout << "Error opening file!" << endl;
return 1;
}
string word;
int number;
// Read a string and a number from the file
inputFile >> word >> number;
cout << "Word: " << word << endl;
cout << "Number: " << number << endl;
inputFile.close();
return 0;
}
The extraction operator skips whitespace, which is useful for reading formatted data.
2. Using getline() for reading lines
#include <fstream>
#include <iostream>
#include <string>
using namespace std;
int main() {
ifstream inputFile("data.txt");
if (!inputFile) {
cout << "Error opening file!" << endl;
return 1;
}
string line;
// Read the file line by line
while (getline(inputFile, line)) {
cout << line << endl;
}
inputFile.close();
return 0;
}
3. Using get() for reading characters
#include <fstream>
#include <iostream>
using namespace std;
int main() {
ifstream inputFile("data.txt");
if (!inputFile) {
cout << "Error opening file!" << endl;
return 1;
}
char ch;
// Read the file character by character
while (inputFile.get(ch)) {
cout << ch;
}
inputFile.close();
return 0;
}
4. Reading binary data
#include <fstream>
#include <iostream>
using namespace std;
struct Person {
char name[50];
int age;
double salary;
};
int main() {
ifstream inputFile("people.dat", ios::binary);
if (!inputFile) {
cout << "Error opening file!" << endl;
return 1;
}
Person person;
// Read a Person object from the file
inputFile.read(reinterpret_cast<char*>(&person), sizeof(Person));
cout << "Name: " << person.name << endl;
cout << "Age: " << person.age << endl;
cout << "Salary: " << person.salary << endl;
inputFile.close();
return 0;
}
Writing to a File
Similar to reading, there are multiple ways to write data to a file:
1. Using insertion operator (<<)
#include <fstream>
#include <iostream>
using namespace std;
int main() {
ofstream outputFile("output.txt");
if (!outputFile) {
cout << "Error opening file!" << endl;
return 1;
}
string name = "John Doe";
int age = 25;
// Write formatted data to the file
outputFile << "Name: " << name << endl;
outputFile << "Age: " << age << endl;
outputFile.close();
return 0;
}
2. Using put() for writing characters
#include <fstream>
#include <iostream>
#include <string>
using namespace std;
int main() {
ofstream outputFile("output.txt");
if (!outputFile) {
cout << "Error opening file!" << endl;
return 1;
}
string message = "Hello, world!";
// Write the string character by character
for (char ch : message) {
outputFile.put(ch);
}
outputFile.close();
return 0;
}
3. Writing binary data
#include <fstream>
#include <iostream>
#include <cstring>
using namespace std;
struct Person {
char name[50];
int age;
double salary;
};
int main() {
ofstream outputFile("people.dat", ios::binary);
if (!outputFile) {
cout << "Error opening file!" << endl;
return 1;
}
Person person;
strcpy(person.name, "Alice Johnson");
person.age = 30;
person.salary = 75000.50;
// Write a Person object to the file
outputFile.write(reinterpret_cast<char*>(&person), sizeof(Person));
outputFile.close();
return 0;
}
File Pointers and Random Access
C++ provides functions to move the file pointer, allowing you to access data at any position in the file:
1. tellg() and tellp()
These functions return the current position of the file pointer:
tellg(): Returns the position of the get pointer (for reading)tellp(): Returns the position of the put pointer (for writing)
2. seekg() and seekp()
These functions move the file pointer to a specified position:
seekg(): Moves the get pointerseekp(): Moves the put pointer
#include <fstream>
#include <iostream>
using namespace std;
int main() {
fstream file("data.txt", ios::in | ios::out);
if (!file) {
cout << "Error opening file!" << endl;
return 1;
}
// Get the current position
streampos currentPos = file.tellg();
cout << "Current position: " << currentPos << endl;
// Move to the 10th byte in the file
file.seekg(10, ios::beg);
// Move 5 bytes forward from the current position
file.seekg(5, ios::cur);
// Move 3 bytes backward from the end of the file
file.seekg(-3, ios::end);
file.close();
return 0;
}
The second parameter in seekg() and seekp() specifies the reference point:
ios::beg: Beginning of the fileios::cur: Current positionios::end: End of the file
Practical Examples
Example 1: Copying a file
#include <fstream>
#include <iostream>
using namespace std;
int main() {
ifstream sourceFile("source.txt");
ofstream destFile("destination.txt");
if (!sourceFile || !destFile) {
cout << "Error opening files!" << endl;
return 1;
}
char ch;
// Read characters from the source file and write them to the destination file
while (sourceFile.get(ch)) {
destFile.put(ch);
}
cout << "File copied successfully!" << endl;
sourceFile.close();
destFile.close();
return 0;
}
Example 2: Counting characters, words, and lines in a file
#include <fstream>
#include <iostream>
#include <string>
using namespace std;
int main() {
ifstream file("data.txt");
if (!file) {
cout << "Error opening file!" << endl;
return 1;
}
int charCount = 0;
int wordCount = 0;
int lineCount = 0;
string line;
bool inWord = false;
char ch;
while (file.get(ch)) {
charCount++;
// Count words
if (isspace(ch)) {
inWord = false;
} else if (!inWord) {
inWord = true;
wordCount++;
}
// Count lines
if (ch == '\n') {
lineCount++;
}
}
// If the file doesn't end with a newline, increment the line count
if (charCount > 0 && ch != '\n') {
lineCount++;
}
cout << "Character count: " << charCount << endl;
cout << "Word count: " << wordCount << endl;
cout << "Line count: " << lineCount << endl;
file.close();
return 0;
}
Example 3: Reading and writing records
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
struct Student {
int id;
string name;
float gpa;
};
void writeStudents(const vector<Student>& students, const string& filename) {
ofstream outFile(filename);
if (!outFile) {
cout << "Error opening file for writing!" << endl;
return;
}
for (const auto& student : students) {
outFile << student.id << "," << student.name << "," << student.gpa << endl;
}
outFile.close();
cout << "Students written to file successfully!" << endl;
}
vector<Student> readStudents(const string& filename) {
ifstream inFile(filename);
vector<Student> students;
if (!inFile) {
cout << "Error opening file for reading!" << endl;
return students;
}
string line;
while (getline(inFile, line)) {
Student student;
size_t pos1 = line.find(',');
size_t pos2 = line.find(',', pos1 + 1);
if (pos1 != string::npos && pos2 != string::npos) {
student.id = stoi(line.substr(0, pos1));
student.name = line.substr(pos1 + 1, pos2 - pos1 - 1);
student.gpa = stof(line.substr(pos2 + 1));
students.push_back(student);
}
}
inFile.close();
return students;
}
void displayStudents(const vector<Student>& students) {
cout << "\nStudent Records:" << endl;
cout << "-----------------------------------------" << endl;
cout << "ID\tName\t\tGPA" << endl;
cout << "-----------------------------------------" << endl;
for (const auto& student : students) {
cout << student.id << "\t" << student.name << "\t\t" << student.gpa << endl;
}
}
int main() {
vector<Student> students = {
{1001, "John Smith", 3.75},
{1002, "Mary Johnson", 3.92},
{1003, "Robert Brown", 3.48}
};
// Write students to file
writeStudents(students, "students.csv");
// Read students from file
vector<Student> loadedStudents = readStudents("students.csv");
// Display loaded students
displayStudents(loadedStudents);
return 0;
}
Error Handling in File Operations
When working with files, it’s important to handle potential errors:
1. Checking if a file was opened successfully
ifstream file("data.txt");
if (!file) {
cerr << "Error: Unable to open file!" << endl;
return 1;
}
2. Using is_open()
ifstream file;
file.open("data.txt");
if (!file.is_open()) {
cerr << "Error: Unable to open file!" << endl;
return 1;
}
3. Checking for end of file (EOF)
while (!file.eof()) {
// Read data from file
}
However, it’s often better to check the result of the read operation directly:
string line;
while (getline(file, line)) {
// Process the line
}
4. Checking for other errors
if (file.fail()) {
cerr << "A reading error occurred!" << endl;
}
if (file.bad()) {
cerr << "A fatal error occurred!" << endl;
}
Best Practices for File Handling
-
Always check if a file was opened successfully before performing operations on it.
-
Close files when you’re done with them to free up system resources.
-
Use appropriate error handling to make your programs robust.
-
Use binary mode for non-text files to avoid issues with special characters.
-
Be careful with file paths. Use relative paths when possible, and remember that backslashes in Windows paths need to be escaped in C++ strings.
-
Handle exceptions that might occur during file operations.
#include <fstream>
#include <iostream>
#include <stdexcept>
using namespace std;
void processFile(const string& filename) {
try {
ifstream file(filename);
if (!file) {
throw runtime_error("Cannot open file: " + filename);
}
// Process the file...
file.close();
} catch (const exception& e) {
cerr << "Error: " << e.what() << endl;
}
}
Summary
File handling in C++ allows you to work with external files for storing and retrieving data. The main classes for file operations are ifstream, ofstream, and fstream, which provide various methods for reading, writing, and navigating through files.
Understanding file handling is essential for developing applications that need to persist data, process large datasets, or interact with external systems through files.