Progress8 of 20 topics

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:

ClassDescriptionHeader
ifstreamInput file stream - for reading from files#include <fstream>
ofstreamOutput file stream - for writing to files#include <fstream>
fstreamFile stream - for both reading and writing#include <fstream>

These classes inherit from more general stream classes:

  • ifstream inherits from istream (like cin)
  • ofstream inherits from ostream (like cout)
  • fstream inherits from both istream and ostream

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

cpp
1#include <iostream>
2#include <fstream>
3using namespace std;
4
5int main() {
6 // Open a file for reading
7 ifstream inputFile("example.txt");
8
9 // Check if the file was opened successfully
10 if (!inputFile) {
11 cerr << "Error: Unable to open file for reading!" << endl;
12 return 1;
13 }
14
15 // Process the file...
16
17 // Close the file when done
18 inputFile.close();
19
20 // Open a file for writing
21 ofstream outputFile("output.txt");
22
23 if (!outputFile) {
24 cerr << "Error: Unable to open file for writing!" << endl;
25 return 1;
26 }
27
28 // Write to the file...
29
30 // Close the file
31 outputFile.close();
32
33 return 0;
34}

Method 2: Using the open() Method

cpp
1#include <iostream>
2#include <fstream>
3using namespace std;
4
5int main() {
6 ifstream inputFile;
7
8 // Open a file using the open() method
9 inputFile.open("example.txt");
10
11 if (!inputFile.is_open()) {
12 cerr << "Error: Could not open file!" << endl;
13 return 1;
14 }
15
16 // Process the file...
17
18 // Close the file
19 inputFile.close();
20
21 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) or if (!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

cpp
1#include <iostream>
2#include <fstream>
3using namespace std;
4
5int main() {
6 ifstream file("example.txt");
7
8 if (!file) {
9 cerr << "Error opening file!" << endl;
10 return 1;
11 }
12
13 char ch;
14 // Read file character by character
15 while (file.get(ch)) {
16 cout << ch;
17 }
18
19 file.close();
20 return 0;
21}

Reading Line by Line

cpp
1#include <iostream>
2#include <fstream>
3#include <string>
4using namespace std;
5
6int main() {
7 ifstream file("example.txt");
8
9 if (!file) {
10 cerr << "Error opening file!" << endl;
11 return 1;
12 }
13
14 string line;
15 // Read file line by line
16 while (getline(file, line)) {
17 cout << line << endl;
18 }
19
20 file.close();
21 return 0;
22}

Reading Formatted Data

cpp
1#include <iostream>
2#include <fstream>
3#include <string>
4using namespace std;
5
6int main() {
7 ifstream file("data.txt");
8
9 if (!file) {
10 cerr << "Error opening file!" << endl;
11 return 1;
12 }
13
14 // Assuming data.txt contains lines with: name age salary
15 string name;
16 int age;
17 double salary;
18
19 // Read formatted data
20 while (file >> name >> age >> salary) {
21 cout << "Name: " << name << ", Age: " << age << ", Salary: $" << salary << endl;
22 }
23
24 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

cpp
1#include <iostream>
2#include <fstream>
3using namespace std;
4
5int main() {
6 ofstream file("output.txt");
7
8 if (!file) {
9 cerr << "Error: Could not open file for writing!" << endl;
10 return 1;
11 }
12
13 // Write text to the file
14 file << "Hello, World!" << endl;
15 file << "This is a second line." << endl;
16
17 // Write numeric data
18 int num = 42;
19 double pi = 3.14159;
20
21 file << "The answer is " << num << " and PI is " << pi << endl;
22
23 file.close();
24 cout << "Data has been written to output.txt" << endl;
25
26 return 0;
27}

Writing Formatted Output

cpp
1#include <iostream>
2#include <fstream>
3#include <iomanip> // For output manipulators
4using namespace std;
5
6int main() {
7 ofstream file("formatted.txt");
8
9 if (!file) {
10 cerr << "Error: Could not open file for writing!" << endl;
11 return 1;
12 }
13
14 // Set up formatting
15 file << fixed << setprecision(2); // Fixed point notation with 2 decimal places
16
17 // Write a table header
18 file << setw(10) << "Name" << setw(8) << "Age" << setw(10) << "Salary" << endl;
19 file << string(28, '-') << endl; // Divider line
20
21 // Write formatted data
22 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;
25
26 file.close();
27 cout << "Formatted data has been written to formatted.txt" << endl;
28
29 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.

ModeDescription
ios::inOpen for input operations (default for ifstream)
ios::outOpen for output operations (default for ofstream)
ios::appAppend to the end of the file
ios::ateSet the initial position to the end of the file
ios::truncIf the file exists, its contents will be truncated before opening
ios::binaryOpen in binary mode (instead of text mode)

These modes can be combined using the bitwise OR operator (|).

cpp
1#include <iostream>
2#include <fstream>
3using namespace std;
4
5int main() {
6 // Open for both reading and writing
7 fstream file("data.txt", ios::in | ios::out);
8
9 // Open for appending
10 ofstream appendFile("log.txt", ios::app);
11 appendFile << "New log entry" << endl;
12
13 // Open in binary mode
14 fstream binaryFile("data.bin", ios::in | ios::out | ios::binary);
15
16 // Open for output, truncating any existing content
17 ofstream newFile("example.txt", ios::out | ios::trunc);
18
19 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 occurred
  • eof(): Returns true if end-of-file has been reached
  • fail(): Returns true if an operation has failed
  • bad(): Returns true if a more serious error has occurred
cpp
1#include <iostream>
2#include <fstream>
3using namespace std;
4
5int main() {
6 ifstream file("example.txt");
7
8 if (!file) {
9 cerr << "Failed to open the file." << endl;
10 return 1;
11 }
12
13 int value;
14 file >> value;
15
16 if (file.fail()) {
17 cerr << "Failed to read an integer from the file." << endl;
18 // Clear the error state
19 file.clear();
20 // Skip the problematic input
21 file.ignore(numeric_limits<streamsize>::max(), '\n');
22 }
23
24 // Check different states
25 if (file.good()) {
26 cout << "Stream state is good" << endl;
27 }
28
29 if (file.eof()) {
30 cout << "Reached end of file" << endl;
31 }
32
33 if (file.fail()) {
34 cout << "An operation failed" << endl;
35 }
36
37 if (file.bad()) {
38 cout << "A serious error occurred" << endl;
39 }
40
41 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:

cpp
1#include <iostream>
2#include <fstream>
3using namespace std;
4
5int main() {
6 fstream file("data.txt", ios::in | ios::out);
7
8 if (!file) {
9 cerr << "Error opening file!" << endl;
10 return 1;
11 }
12
13 // Get the current position
14 streampos currentPos = file.tellg();
15 cout << "Current position: " << currentPos << endl;
16
17 // Move to a specific position (from beginning)
18 file.seekg(10, ios::beg);
19
20 // Move 5 bytes forward from current position
21 file.seekg(5, ios::cur);
22
23 // Move 10 bytes backward from the end
24 file.seekg(-10, ios::end);
25
26 // Setting position for writing (similar methods)
27 file.seekp(0, ios::beg); // Move to the beginning for writing
28
29 // Write at the current position
30 file << "New text";
31
32 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 file
  • ios::cur: Current position
  • ios::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

cpp
1#include <iostream>
2#include <fstream>
3#include <vector>
4using namespace std;
5
6struct Person {
7 char name[50];
8 int age;
9 double salary;
10};
11
12int main() {
13 // Writing binary data
14 ofstream outFile("data.bin", ios::binary);
15
16 if (!outFile) {
17 cerr << "Error opening file for writing!" << endl;
18 return 1;
19 }
20
21 // Writing a simple integer
22 int num = 42;
23 outFile.write(reinterpret_cast<char*>(&num), sizeof(num));
24
25 // Writing an array
26 int numbers[5] = {10, 20, 30, 40, 50};
27 outFile.write(reinterpret_cast<char*>(numbers), sizeof(numbers));
28
29 // Writing a struct
30 Person person = {"John Doe", 30, 75000.50};
31 outFile.write(reinterpret_cast<char*>(&person), sizeof(Person));
32
33 outFile.close();
34
35 cout << "Binary data written successfully." << endl;
36 return 0;
37}

Reading Binary Data

cpp
1#include <iostream>
2#include <fstream>
3using namespace std;
4
5struct Person {
6 char name[50];
7 int age;
8 double salary;
9};
10
11int main() {
12 // Reading binary data
13 ifstream inFile("data.bin", ios::binary);
14
15 if (!inFile) {
16 cerr << "Error opening file for reading!" << endl;
17 return 1;
18 }
19
20 // Reading a simple integer
21 int num;
22 inFile.read(reinterpret_cast<char*>(&num), sizeof(num));
23 cout << "Integer: " << num << endl;
24
25 // Reading an array
26 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;
33
34 // Reading a struct
35 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;
40
41 inFile.close();
42
43 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 and char* for read() and write()
  • Always specify ios::binary when working with binary files

Practical Examples

Example 1: Copying a File

cpp
1#include <iostream>
2#include <fstream>
3using namespace std;
4
5int main() {
6 ifstream sourceFile("source.txt", ios::binary);
7 ofstream destFile("destination.txt", ios::binary);
8
9 if (!sourceFile) {
10 cerr << "Error opening source file!" << endl;
11 return 1;
12 }
13
14 if (!destFile) {
15 cerr << "Error opening destination file!" << endl;
16 sourceFile.close();
17 return 1;
18 }
19
20 // Copy file contents
21 destFile << sourceFile.rdbuf();
22
23 // Check if any errors occurred
24 if (!destFile) {
25 cerr << "Error writing to destination file!" << endl;
26 return 1;
27 }
28
29 cout << "File copied successfully." << endl;
30
31 sourceFile.close();
32 destFile.close();
33
34 return 0;
35}

Example 2: CSV File Processing

cpp
1#include <iostream>
2#include <fstream>
3#include <sstream>
4#include <string>
5#include <vector>
6using namespace std;
7
8struct Employee {
9 string name;
10 int id;
11 double salary;
12};
13
14int main() {
15 // Reading a CSV file
16 ifstream inFile("employees.csv");
17
18 if (!inFile) {
19 cerr << "Error opening file!" << endl;
20 return 1;
21 }
22
23 vector<Employee> employees;
24 string line;
25
26 // Skip header line
27 getline(inFile, line);
28
29 // Read data line by line
30 while (getline(inFile, line)) {
31 stringstream ss(line);
32 string token;
33 Employee emp;
34
35 // Parse CSV values
36 getline(ss, token, ',');
37 emp.name = token;
38
39 getline(ss, token, ',');
40 emp.id = stoi(token);
41
42 getline(ss, token, ',');
43 emp.salary = stod(token);
44
45 employees.push_back(emp);
46 }
47
48 inFile.close();
49
50 // Process the data
51 cout << "Employee data:" << endl;
52 for (const auto& emp : employees) {
53 cout << "Name: " << emp.name << ", ID: " << emp.id
54 << ", Salary: " << emp.salary << endl;
55 }
56
57 // Calculate average salary
58 double totalSalary = 0;
59 for (const auto& emp : employees) {
60 totalSalary += emp.salary;
61 }
62
63 double avgSalary = totalSalary / employees.size();
64 cout << "Average salary: " << avgSalary << endl;
65
66 // Write results to a new file
67 ofstream outFile("salary_report.txt");
68
69 if (!outFile) {
70 cerr << "Error creating report file!" << endl;
71 return 1;
72 }
73
74 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;
80
81 for (const auto& emp : employees) {
82 if (emp.salary > avgSalary) {
83 outFile << emp.name << " (" << emp.id << "): " << emp.salary << endl;
84 }
85 }
86
87 outFile.close();
88
89 cout << "Report generated: salary_report.txt" << endl;
90
91 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 more

Understand how to handle errors in C++.

Learn more

Explore the Standard Template Library.

Learn more