BIT153 Object Oriented Programming

Object Oriented ProgrammingUnit 814 min read

File Handling in C++: Streams, File Operations & Error Handling

Unit 8 of Object Oriented Programming: Explores C++ file handling using streams (ifstream, ofstream, fstream), file operations (reading/writing text/binary), error handling, and real-world applications like data persistence in apps, transaction logs, and user-generated content storage.

TAKEAWAYS:

  • File handling in C++ uses streams (ifstream, ofstream, fstream) to read/write data sequentially, enabling persistent storage of program data.
  • Text files store data as ASCII/Unicode characters, while binary files store raw bytes (e.g., images, serialized objects) for faster access and space efficiency.
  • Error handling (e.g., fail(), eof(), bad()) ensures robustness when files are missing, corrupted, or inaccessible.
  • Common operations include opening/closing files, sequential/random access, and appending data, critical for logging, databases, and user data.
  • Real-world use: Apps like eSewa store transaction logs in files, Daraz saves order histories, and NTC manages call records—all using file handling.
  • Memory management is implicit in file streams (auto-closed on destruction), but explicit close() improves performance for large files.

1. Introduction to File Handling

File handling allows programs to store and retrieve data permanently, even after termination. Unlike variables (stored in RAM), files persist on secondary storage (SSD/HDD). Key concepts:

  • File: A named collection of data stored on disk.
  • Stream: A sequence of bytes (text/binary) flowing into/out of a file (or device like keyboard/monitor).
  • File Operations: Open, read, write, append, close, seek (random access).
Data ExecutionWrite (Save)Persistent StoragePersistent StorageRead (Load)RAMDiskFile 1File 2Program
Data flow between RAM and disk files during program execution (e.g., saving student records to 'students.txt').

1.1 Streams in C++

C++ uses stream classes from <fstream> to interact with files:

classDiagram
    class Stream {
        +open() string
        +close()
        +read() char
        +write() char
        +eof() bool
        +fail() bool
    }
    class ifstream {
        +read text files
    }
    class ofstream {
        +write text files
    }
    class fstream {
        +read/write text/binary
    }
    Stream <|-- ifstream
    Stream <|-- ofstream
    Stream <|-- fstream

Key Stream Classes:

Class Purpose Example Usage
ifstream Read-only text files ifstream fin("data.txt");
ofstream Write-only text files ofstream fout("output.txt");
fstream Read/write text/binary files fstream file("data.bin", ios::binary);

1.2 Opening and Closing Files

  • Opening: Use open(filename, mode) or constructor syntax.

    ifstream fin("input.txt"); // Default: text mode, read-only
    ofstream fout("output.txt", ios::app); // Append mode
    
  • Modes:

    • ios::in : Open for input (default for ifstream).
    • ios::out : Open for output (default for ofstream).
    • ios::app : Append to end of file.
    • ios::trunc: Truncate file before opening (default for ofstream).
    • ios::binary: Binary mode (no text mode conversions).
  • Closing: Files auto-close when stream object is destroyed, but explicit close() is better for large files.

    fin.close(); // Good practice
    

1.3 Checking File Status

Use member functions to verify file operations:

graph TD
  A["File Operation"] --> B{"fail()"}
  B -->|"true"| C["Error occurred"]
  B -->|"false"| D{"good()"}
  D -->|"true"| E["Operation succeeded"]
  D -->|"false"| F{"eof()"}
  F -->|"true"| G["End of file reached"]
  F -->|"false"| H["Data available"]
  E -->|"Check with"| I["!fail() && good()"]
  caption "Status checking flow in C++ file streams"

Example:

ifstream fin("data.txt");
if (!fin.is_open()) {
    cerr << "Error opening file!" << endl;
    return 1;
}

2. Reading from Files

2.1 Reading Text Files

Use >> operator to read line-by-line or character-by-character.

Example: Read a Text File

#include <fstream>
#include <iostream>
using namespace std;

int main() {
    ifstream fin("students.txt");
    if (!fin.is_open()) {
        cerr << "Failed to open file!" << endl;
        return 1;
    }

    string line;
    while (getline(fin, line)) { // Read entire line
        cout << line << endl;
    }
    fin.close();
    return 0;
}

Trace:

Step Action State of fin
1 fin.open("students.txt") File opened, pointer at start
2 getline(fin, line) Reads "Alice,20" → line = "Alice,20"
3 getline(fin, line) Reads "Bob,21" → line = "Bob,21"
4 fin.eof() Returns true (end of file)

2.2 Reading Binary Files

Binary files store raw bytes (e.g., serialized objects). Use read() or write() with ios::binary.

Example: Read Binary Data

#include <fstream>
struct Student {
    char name[50];
    int age;
};
int main() {
    ifstream fin("students.dat", ios::binary);
    Student s;
    while (fin.read(reinterpret_cast<char*>(&s), sizeof(s))) {
        cout << s.name << ", " << s.age << endl;
    }
    fin.close();
    return 0;
}

Visualization:

Binary File Structure:
+---------------------+---------------------+
| Student 1 (Alice)   | Student 2 (Bob)     |
| [Name: Alice, Age:20]| [Name: Bob, Age:21]  |
+---------------------+---------------------+

3. Writing to Files

sequenceDiagram
  participant Program
  participant File
  Program->>File: fout.open("output.txt")
  File-->>Program: File opened
  Program->>File: fout << "Hello, File Handling!"
  Program->>File: fout << "\nLine 2"
  Program->>File: fout.close()
  File-->>Program: File persisted
  caption "Step-by-step writing process to output.txt"

3.1 Writing Text Files

Use << operator to write data sequentially.

Hello, World!Line 2Final lineTOP
Buffer state during sequential writing to a text file

Example: Write to a Text File

#include <fstream>
#include <iostream>
using namespace std;

int main() {
    ofstream fout("output.txt");
    if (!fout.is_open()) {
        cerr << "Failed to open file!" << endl;
        return 1;
    }
    fout << "Hello, File Handling!" << endl;
    fout << "Line 2" << endl;
    fout.close();
    return 0;
}

State After Writing:

File "output.txt":
Hello, File Handling!
Line 2

3.2 Writing Binary Files

Use write() to store structured data (e.g., objects).

Example: Write Binary Data

#include <fstream>
struct Student {
    char name[50];
    int age;
};
int main() {
    ofstream fout("students.dat", ios::binary);
    Student s1 = {"Alice", 20};
    Student s2 = {"Bob", 21};
    fout.write(reinterpret_cast<char*>(&s1), sizeof(s1));
    fout.write(reinterpret_cast<char*>(&s2), sizeof(s2));
    fout.close();
    return 0;
}

Binary File Layout:

+-----------+-----------+-----------+-----------+
| Alice (50)| 20 (int)  | Bob (50)  | 21 (int)  |
+-----------+-----------+-----------+-----------+

4. Random Access in Files

Use seekg() (input) and seekp() (output) to navigate files.

Example: Jump to Position

#include <fstream>
int main() {
    fstream file("data.bin", ios::in | ios::out | ios::binary);
    file.seekg(0, ios::end); // Move to end
    int size = file.tellg(); // Get file size
    cout << "File size: " << size << " bytes" << endl;
    file.seekg(10); // Move to byte 10
    char ch;
    file.read(&ch, 1);
    cout << "Byte at 10: " << ch << endl;
    file.close();
    return 0;
}

5. Error Handling in File Handling

File operations can fail (e.g., disk full, permission denied). Use:

  • fail(): Checks for errors (e.g., corrupt data).
  • eof(): Checks end-of-file (not for errors!).
  • bad(): Checks for unrecoverable errors (e.g., disk failure).

Example: Robust File Reading

#include <fstream>
#include <iostream>
using namespace std;

int main() {
    ifstream fin("data.txt");
    if (!fin.is_open()) {
        cerr << "Error opening file!" << endl;
        return 1;
    }
    string line;
    while (getline(fin, line)) {
        if (fin.fail()) {
            cerr << "Error reading line!" << endl;
            break;
        }
        cout << line << endl;
    }
    fin.close();
    return 0;
}

6. File Handling in Real-World Applications

In the Real World

  1. eSewa Transaction Logs

    • Idea: eSewa stores every transaction (e.g., "User X paid Y NPR to User Z") in a text file with timestamps.
    • How: Uses ofstream to append logs in real-time.
    • Example: When you pay a merchant, eSewa writes:
      2024-05-20 12:34:56 | User123 | 500 | Merchant456 | Success
      
  2. Daraz Order Processing

    • Idea: Daraz saves order details (customer, items, status) in a binary file for faster retrieval.
    • How: Uses fstream with ios::binary to store structured data (e.g., Order objects).
    • Worked Example: Suppose Daraz receives 100 orders/day. A binary file stores each order as:
      [OrderID:1001, Customer:Alice, Items:[Laptop,Phone], Status:Shipped]
      
      To check Alice’s order status, Daraz reads the binary file sequentially or uses seekg() to jump to her record.
  3. NTC Call Records

    • Idea: NTC logs call data (e.g., duration, time, numbers) in comma-separated text files for billing.
    • How: Uses ofstream to append records daily.
    • Example File Snippet:
      9841234567,9812345678,2024-05-20,10:30,120
      

7. Advantages and Disadvantages

Advantages Disadvantages
Persistent storage (survives program termination). Slower than RAM access.
Large data storage (e.g., databases). File corruption risk (e.g., power loss).
Shared across multiple programs. Requires error handling (e.g., fail()).
Supports text/binary formats. Manual management (e.g., closing files).

8. Comparison: Text vs. Binary Files

Feature Text Files Binary Files
Format Human-readable (ASCII/Unicode). Machine-readable (raw bytes).
Speed Slower (conversions needed). Faster (direct byte access).
Size Larger (extra characters for formatting). Smaller (no overhead).
Use Case Logs, config files. Databases, images, serialized objects.
Example students.txt students.dat

9. Worked Example: Copy File 1 to File 2

Problem: Write a program to copy the contents of source.txt to destination.txt.

Solution:

#include <fstream>
#include <iostream>
using namespace std;

int main() {
    ifstream source("source.txt");
    ofstream dest("destination.txt");

    if (!source.is_open() || !dest.is_open()) {
        cerr << "Error opening files!" << endl;
        return 1;
    }

    char ch;
    while (source.get(ch)) { // Read character-by-character
        dest.put(ch);        // Write to destination
    }

    source.close();
    dest.close();
    cout << "File copied successfully!" << endl;
    return 0;
}

Trace:

Step Action source State dest State
1 source.open("source.txt") Open, pointer at start Open, empty
2 source.get(ch) ch = 'H' (first char) -
3 dest.put(ch) ch = 'e' Writes 'H' to file
... ... ... ...
10 source.eof() true (end reached) File fully copied

10. Exam Tips

  1. Understand Stream Classes:

    • Know the difference between ifstream, ofstream, and fstream. Always check if files are opened (is_open()).
  2. Error Handling:

    • Use fail(), eof(), and bad() to handle errors gracefully. Example:
      if (fin.fail()) { cerr << "Read error!" << endl; }
      
  3. Binary vs. Text:

    • Text files are human-readable but slower; binary files are faster but require ios::binary mode.
  4. Random Access:

    • Use seekg() and seekp() for direct access (e.g., jumping to a record in a database).
  5. Real-World Mapping:

    • Link file handling to apps like eSewa logs, Daraz orders, or NTC call records. Explain how streams are used.
  6. Code Structure:

    • Always include <fstream> and use using namespace std; (or qualify with std::).
    • Close files explicitly (close()) for large files to free resources.
  7. Common Pitfalls:

    • Forgetting to check is_open() before operations.
    • Mixing text and binary modes without ios::binary.
    • Not handling fail() or bad() errors.

Sample Exam Question:

Write a program to read a text file containing student names and ages, then write them to another file in reverse order (last student first).

Solution:

#include <fstream>
#include <vector>
#include <algorithm>
using namespace std;

struct Student {
    string name;
    int age;
};

int main() {
    ifstream fin("input.txt");
    ofstream fout("output.txt");
    vector<Student> students;

    if (!fin.is_open() || !fout.is_open()) {
        cerr << "Error opening files!" << endl;
        return 1;
    }

    string line;
    while (getline(fin, line)) {
        size_t comma = line.find(',');
        Student s;
        s.name = line.substr(0, comma);
        s.age = stoi(line.substr(comma + 1));
        students.push_back(s);
    }

    reverse(students.begin(), students.end());
    for (const auto& s : students) {
        fout << s.name << "," << s.age << endl;
    }

    fin.close();
    fout.close();
    return 0;
}

Trace:

  1. Reads Alice,20 → Bob,21 → Charlie,19 into students.
  2. Reverses the vector: Charlie,19 → Bob,21 → Alice,20.
  3. Writes to output.txt in reverse order.

Based on the TU BIT syllabus for Object Oriented Programming (BIT153), unit 8.

Discussion

Loading…