40% complete
File I/O in C++
File input/output (I/O) is an essential aspect of programming that allows you to store data permanently, exchange information between programs, and work with larger datasets that can't be efficiently handled in memory alone.
What You'll Learn
- Understanding file streams in C++
- Opening and closing files
- Reading from text files
- Writing to text files
- File modes and flags
- Error handling during file operations
- Working with binary files
- File positioning and navigation
Introduction to File Streams
C++ provides file handling through stream classes that are part of the Standard Library. These classes extend the same concepts used for console I/O (like cin
and cout
) to files. The primary stream classes for file operations are:
Class | Description | Header |
---|---|---|
ifstream | Input file stream - for reading from files | #include <fstream> |
ofstream | Output file stream - for writing to files | #include <fstream> |
fstream | File stream - for both reading and writing | #include <fstream> |
These classes inherit from more general stream classes:
ifstream
inherits fromistream
(likecin
)ofstream
inherits fromostream
(likecout
)fstream
inherits from bothistream
andostream
Opening and Closing Files
Before you can read from or write to a file, you need to open it. There are two common ways to open a file:
Method 1: Using the Constructor
1#include <iostream>2#include <fstream>3using namespace std;45int main() {6 // Open a file for reading7 ifstream inputFile("example.txt");89 // Check if the file was opened successfully10 if (!inputFile) {11 cerr << "Error: Unable to open file for reading!" << endl;12 return 1;13 }1415 // Process the file...1617 // Close the file when done18 inputFile.close();1920 // Open a file for writing21 ofstream outputFile("output.txt");2223 if (!outputFile) {24 cerr << "Error: Unable to open file for writing!" << endl;25 return 1;26 }2728 // Write to the file...2930 // Close the file31 outputFile.close();3233 return 0;34}
Method 2: Using the open() Method
1#include <iostream>2#include <fstream>3using namespace std;45int main() {6 ifstream inputFile;78 // Open a file using the open() method9 inputFile.open("example.txt");1011 if (!inputFile.is_open()) {12 cerr << "Error: Could not open file!" << endl;13 return 1;14 }1516 // Process the file...1718 // Close the file19 inputFile.close();2021 return 0;22}
Important
Always check if a file was opened successfully before attempting to read from or write to it. This can be done using:
- Implicit boolean conversion:
if (fileStream)
orif (!fileStream)
- The
is_open()
method:if (fileStream.is_open())
Reading from Files
Once you've successfully opened a file for reading, there are several ways to extract data from it.
Reading Character by Character
1#include <iostream>2#include <fstream>3using namespace std;45int main() {6 ifstream file("example.txt");78 if (!file) {9 cerr << "Error opening file!" << endl;10 return 1;11 }1213 char ch;14 // Read file character by character15 while (file.get(ch)) {16 cout << ch;17 }1819 file.close();20 return 0;21}
Reading Line by Line
1#include <iostream>2#include <fstream>3#include <string>4using namespace std;56int main() {7 ifstream file("example.txt");89 if (!file) {10 cerr << "Error opening file!" << endl;11 return 1;12 }1314 string line;15 // Read file line by line16 while (getline(file, line)) {17 cout << line << endl;18 }1920 file.close();21 return 0;22}
Reading Formatted Data
1#include <iostream>2#include <fstream>3#include <string>4using namespace std;56int main() {7 ifstream file("data.txt");89 if (!file) {10 cerr << "Error opening file!" << endl;11 return 1;12 }1314 // Assuming data.txt contains lines with: name age salary15 string name;16 int age;17 double salary;1819 // Read formatted data20 while (file >> name >> age >> salary) {21 cout << "Name: " << name << ", Age: " << age << ", Salary: $" << salary << endl;22 }2324 file.close();25 return 0;26}
Input Stream Extraction Operators
When using file >> variable
, keep in mind:
- It skips whitespace (spaces, tabs, newlines)
- It reads until the next whitespace character
- It returns
false
when it reaches the end of file or encounters an error - It can fail if the data doesn't match the expected type
Writing to Files
Writing to files is similar to using cout
for console output, but with an ofstream
object.
Basic File Writing
1#include <iostream>2#include <fstream>3using namespace std;45int main() {6 ofstream file("output.txt");78 if (!file) {9 cerr << "Error: Could not open file for writing!" << endl;10 return 1;11 }1213 // Write text to the file14 file << "Hello, World!" << endl;15 file << "This is a second line." << endl;1617 // Write numeric data18 int num = 42;19 double pi = 3.14159;2021 file << "The answer is " << num << " and PI is " << pi << endl;2223 file.close();24 cout << "Data has been written to output.txt" << endl;2526 return 0;27}
Writing Formatted Output
1#include <iostream>2#include <fstream>3#include <iomanip> // For output manipulators4using namespace std;56int main() {7 ofstream file("formatted.txt");89 if (!file) {10 cerr << "Error: Could not open file for writing!" << endl;11 return 1;12 }1314 // Set up formatting15 file << fixed << setprecision(2); // Fixed point notation with 2 decimal places1617 // Write a table header18 file << setw(10) << "Name" << setw(8) << "Age" << setw(10) << "Salary" << endl;19 file << string(28, '-') << endl; // Divider line2021 // Write formatted data22 file << setw(10) << "John" << setw(8) << 25 << setw(10) << 1250.50 << endl;23 file << setw(10) << "Alice" << setw(8) << 32 << setw(10) << 1800.75 << endl;24 file << setw(10) << "Bob" << setw(8) << 41 << setw(10) << 2100.00 << endl;2526 file.close();27 cout << "Formatted data has been written to formatted.txt" << endl;2829 return 0;30}
File Modes
When opening a file, you can specify a mode that determines how the file will be treated. These modes are defined as constants in the ios
class.
Mode | Description |
---|---|
ios::in | Open for input operations (default for ifstream) |
ios::out | Open for output operations (default for ofstream) |
ios::app | Append to the end of the file |
ios::ate | Set the initial position to 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) |
These modes can be combined using the bitwise OR operator (|
).
1#include <iostream>2#include <fstream>3using namespace std;45int main() {6 // Open for both reading and writing7 fstream file("data.txt", ios::in | ios::out);89 // Open for appending10 ofstream appendFile("log.txt", ios::app);11 appendFile << "New log entry" << endl;1213 // Open in binary mode14 fstream binaryFile("data.bin", ios::in | ios::out | ios::binary);1516 // Open for output, truncating any existing content17 ofstream newFile("example.txt", ios::out | ios::trunc);1819 return 0;20}
Error Handling
When working with files, various errors can occur. It's important to check for these errors and handle them appropriately.
Checking Stream States
File streams have state flags that indicate their current condition:
good()
: Returns true if no errors have occurredeof()
: Returns true if end-of-file has been reachedfail()
: Returns true if an operation has failedbad()
: Returns true if a more serious error has occurred
1#include <iostream>2#include <fstream>3using namespace std;45int main() {6 ifstream file("example.txt");78 if (!file) {9 cerr << "Failed to open the file." << endl;10 return 1;11 }1213 int value;14 file >> value;1516 if (file.fail()) {17 cerr << "Failed to read an integer from the file." << endl;18 // Clear the error state19 file.clear();20 // Skip the problematic input21 file.ignore(numeric_limits<streamsize>::max(), '\n');22 }2324 // Check different states25 if (file.good()) {26 cout << "Stream state is good" << endl;27 }2829 if (file.eof()) {30 cout << "Reached end of file" << endl;31 }3233 if (file.fail()) {34 cout << "An operation failed" << endl;35 }3637 if (file.bad()) {38 cout << "A serious error occurred" << endl;39 }4041 file.close();42 return 0;43}
Common File Errors
- File not found (when opening for reading)
- Permission denied (insufficient access rights)
- Disk full (when writing)
- Format errors (trying to read data in an unexpected format)
- Corrupted file structure
File Positioning
File streams maintain a position indicator that determines where the next read or write operation will occur. You can manipulate this position using several methods:
1#include <iostream>2#include <fstream>3using namespace std;45int main() {6 fstream file("data.txt", ios::in | ios::out);78 if (!file) {9 cerr << "Error opening file!" << endl;10 return 1;11 }1213 // Get the current position14 streampos currentPos = file.tellg();15 cout << "Current position: " << currentPos << endl;1617 // Move to a specific position (from beginning)18 file.seekg(10, ios::beg);1920 // Move 5 bytes forward from current position21 file.seekg(5, ios::cur);2223 // Move 10 bytes backward from the end24 file.seekg(-10, ios::end);2526 // Setting position for writing (similar methods)27 file.seekp(0, ios::beg); // Move to the beginning for writing2829 // Write at the current position30 file << "New text";3132 file.close();33 return 0;34}
Position Reference Points
When seeking positions in a file, you can use three reference points:
ios::beg
: Beginning of the fileios::cur
: Current positionios::end
: End of the file
Binary File I/O
While text files are human-readable, binary files store data in its raw format. Binary I/O is more efficient for storing numeric data, structures, or objects.
Writing Binary Data
1#include <iostream>2#include <fstream>3#include <vector>4using namespace std;56struct Person {7 char name[50];8 int age;9 double salary;10};1112int main() {13 // Writing binary data14 ofstream outFile("data.bin", ios::binary);1516 if (!outFile) {17 cerr << "Error opening file for writing!" << endl;18 return 1;19 }2021 // Writing a simple integer22 int num = 42;23 outFile.write(reinterpret_cast<char*>(&num), sizeof(num));2425 // Writing an array26 int numbers[5] = {10, 20, 30, 40, 50};27 outFile.write(reinterpret_cast<char*>(numbers), sizeof(numbers));2829 // Writing a struct30 Person person = {"John Doe", 30, 75000.50};31 outFile.write(reinterpret_cast<char*>(&person), sizeof(Person));3233 outFile.close();3435 cout << "Binary data written successfully." << endl;36 return 0;37}
Reading Binary Data
1#include <iostream>2#include <fstream>3using namespace std;45struct Person {6 char name[50];7 int age;8 double salary;9};1011int main() {12 // Reading binary data13 ifstream inFile("data.bin", ios::binary);1415 if (!inFile) {16 cerr << "Error opening file for reading!" << endl;17 return 1;18 }1920 // Reading a simple integer21 int num;22 inFile.read(reinterpret_cast<char*>(&num), sizeof(num));23 cout << "Integer: " << num << endl;2425 // Reading an array26 int numbers[5];27 inFile.read(reinterpret_cast<char*>(numbers), sizeof(numbers));28 cout << "Array elements: ";29 for (int i = 0; i < 5; i++) {30 cout << numbers[i] << " ";31 }32 cout << endl;3334 // Reading a struct35 Person person;36 inFile.read(reinterpret_cast<char*>(&person), sizeof(Person));37 cout << "Person name: " << person.name << endl;38 cout << "Person age: " << person.age << endl;39 cout << "Person salary: " << person.salary << endl;4041 inFile.close();4243 return 0;44}
Binary I/O Considerations
- Binary files are not human-readable but are more compact and faster to process
- Binary files may not be portable between different systems (due to endianness, struct padding, etc.)
- Use
reinterpret_cast
to convert between data types andchar*
forread()
andwrite()
- Always specify
ios::binary
when working with binary files
Practical Examples
Example 1: Copying a File
1#include <iostream>2#include <fstream>3using namespace std;45int main() {6 ifstream sourceFile("source.txt", ios::binary);7 ofstream destFile("destination.txt", ios::binary);89 if (!sourceFile) {10 cerr << "Error opening source file!" << endl;11 return 1;12 }1314 if (!destFile) {15 cerr << "Error opening destination file!" << endl;16 sourceFile.close();17 return 1;18 }1920 // Copy file contents21 destFile << sourceFile.rdbuf();2223 // Check if any errors occurred24 if (!destFile) {25 cerr << "Error writing to destination file!" << endl;26 return 1;27 }2829 cout << "File copied successfully." << endl;3031 sourceFile.close();32 destFile.close();3334 return 0;35}
Example 2: CSV File Processing
1#include <iostream>2#include <fstream>3#include <sstream>4#include <string>5#include <vector>6using namespace std;78struct Employee {9 string name;10 int id;11 double salary;12};1314int main() {15 // Reading a CSV file16 ifstream inFile("employees.csv");1718 if (!inFile) {19 cerr << "Error opening file!" << endl;20 return 1;21 }2223 vector<Employee> employees;24 string line;2526 // Skip header line27 getline(inFile, line);2829 // Read data line by line30 while (getline(inFile, line)) {31 stringstream ss(line);32 string token;33 Employee emp;3435 // Parse CSV values36 getline(ss, token, ',');37 emp.name = token;3839 getline(ss, token, ',');40 emp.id = stoi(token);4142 getline(ss, token, ',');43 emp.salary = stod(token);4445 employees.push_back(emp);46 }4748 inFile.close();4950 // Process the data51 cout << "Employee data:" << endl;52 for (const auto& emp : employees) {53 cout << "Name: " << emp.name << ", ID: " << emp.id54 << ", Salary: " << emp.salary << endl;55 }5657 // Calculate average salary58 double totalSalary = 0;59 for (const auto& emp : employees) {60 totalSalary += emp.salary;61 }6263 double avgSalary = totalSalary / employees.size();64 cout << "Average salary: " << avgSalary << endl;6566 // Write results to a new file67 ofstream outFile("salary_report.txt");6869 if (!outFile) {70 cerr << "Error creating report file!" << endl;71 return 1;72 }7374 outFile << "Salary Report" << endl;75 outFile << "-------------" << endl;76 outFile << "Number of employees: " << employees.size() << endl;77 outFile << "Average salary: " << avgSalary << endl;78 outFile << endl;79 outFile << "Employees above average salary:" << endl;8081 for (const auto& emp : employees) {82 if (emp.salary > avgSalary) {83 outFile << emp.name << " (" << emp.id << "): " << emp.salary << endl;84 }85 }8687 outFile.close();8889 cout << "Report generated: salary_report.txt" << endl;9091 return 0;92}
Best Practices
Do
- Always check if files were opened successfully
- Close files when you're done with them
- Use appropriate file modes for your needs
- Handle exceptions and errors gracefully
- Use binary mode for non-text data
- Flush output streams when immediate writing is required
- Use relative paths or configurable file locations
Avoid
- Hardcoding absolute file paths
- Leaving files open longer than necessary
- Ignoring error conditions
- Mixing text and binary operations on the same file
- Using C-style file I/O (
fopen
,fread
, etc.) in C++ code - Assuming file formats without validation
- Reading entire large files into memory unnecessarily
Summary
File I/O is a fundamental skill for C++ programmers, enabling you to save data persistently, process large datasets, and exchange information between programs. In this tutorial, we've covered:
- Working with text and binary files
- Different file stream classes for reading and writing
- Opening and closing files with various modes
- Reading and writing techniques
- Error handling during file operations
- Positioning within files
- Practical examples of file processing
With these skills, you can now effectively work with files in your C++ programs, whether you're creating simple configuration files, processing large datasets, or implementing complex data storage solutions.
Related Tutorials
Learn about string handling in C++.
Learn moreUnderstand how to handle errors in C++.
Learn moreExplore the Standard Template Library.
Learn more