Lesson 8: File Streams & Formatting Manipulators

Lesson Overview

This lesson covers two critical topics for laboratory quizzes and coding tests: File I/O Stream Operations (<fstream>) and Output Formatting Manipulators (<iomanip>). You will learn how to persist data, perform binary I/O, execute random-access byte seeking, and format clean tables on console output.


πŸš€ 1. The β€œWhy” & Concept Breakdown

File Handling Mechanics (<fstream>)

Console input and output are transient in RAM. To persist data {save from volatile RAM to permanent disk storage} across executions, C++ provides three primary stream classes in <fstream>:

  1. ifstream: Input file stream (read-only).
  2. ofstream: Output file stream (write-only; creates file if non-existent).
  3. fstream: General file stream (supports simultaneous read/write).
fstream file;
file.open("records.dat", ios::in | ios::out | ios::binary); // Simultaneous read/write in binary mode

⚑ Files & Formatting: Python vs. C++

  • Auto-Closing: ifstream/ofstream close files automatically when their destructor runs at scope exit } (RAII pattern).
  • Random Access: seekg(offset, ios::beg / cur / end) for read streams; seekp() for write streams.
  • Formatting: Uses <iomanip> manipulators (cout << setw(10) << fixed << setprecision(2)). Note: setw is temporary/one-shot.

File Pointers & Byte Positioning (seekg, tellg)

File streams maintain byte-level index pointers:

  • get pointer: Tracks reading position {queried by tellg(), moved by seekg()}.
  • put pointer: Tracks writing position {queried by tellp(), moved by seekp()}.
file.seekg(offset, refposition); // Seek get pointer
file.seekp(offset, refposition); // Seek put pointer

The refposition takes one of three constants defined in class ios:

  1. ios::beg β€” Offset from the start of the file.
  2. ios::cur β€” Offset from the current pointer location.
  3. ios::end β€” Offset from the end of the file.

Portability Hazard: Relative Seeks on Text Streams

Performing relative seeks (e.g., seekg(-15, ios::cur)) on text-mode streams produces undefined behavior due to invisible line-ending conversions (e.g., Windows \r\n \n). Always open files in binary mode (ios::binary) when using random-access byte seeking.


Output Formatting Manipulators (<iomanip>)

1. setw(int width) (Field Width)

  • Forces the next printed item to occupy a specific column width.
  • Default alignment is right-justified with space padding.
  • ⚠️ The Golden Trap: Unlike other manipulators, setw() is one-shot / temporary. It applies strictly to the single next output item, then immediately reverts to default!

2. setfill(char c) (Padding Character)

  • Replaces default whitespace padding with character c.
  • Persistent: Remains in effect until explicitly changed.

3. setprecision(int p) & fixed

  • setprecision(p) by default controls total significant digits.
  • When combined with fixed (cout << fixed << setprecision(2)), it locks display to exactly p decimal places.
  • Persistent: Remains active until modified.

πŸ”‘ 2. Keyword & Syntax Dictionary

Keyword / ConceptTechnical Purpose & SyntaxExam Example
ios::binaryMode flag: Processes raw bytes rather than ASCII translation.file.open("db.bin", ios::binary);
seekg(8, ios::beg)Moves reading pointer to byte offset 8 from start.infile.seekg(8, ios::beg);
tellg()Returns current absolute byte offset of reading pointer.long pos = infile.tellg();
setw(10)Sets field width for the immediate next output item.cout << setw(10) << id;
setfill('*')Sets padding character persistently.cout << setfill('*');

πŸ’» 3. Step-by-Step Code Evolution

Step 8.1: Binary File I/O (read and write)

#include <iostream>
#include <fstream>
using namespace std;
 
int main() {
    int dataset[3] = {100, 200, 300};
 
    // 1. Binary Output
    ofstream outfile("data.bin", ios::binary);
    outfile.write((char*)&dataset, sizeof(dataset));
    outfile.close();
 
    // 2. Binary Input
    int received[3];
    ifstream infile("data.bin", ios::binary);
    infile.read((char*)&received, sizeof(received));
    infile.close();
 
    cout << "Read index 0: " << received[0] << " (Expected: 100)" << endl;
    return 0;
}

Step 8.2: Random Access Seeking (seekg and tellg)

#include <iostream>
#include <fstream>
using namespace std;
 
int main() {
    ifstream infile("data.bin", ios::binary);
    if (!infile) {
        cout << "Error opening file!" << endl;
        return 1;
    }
 
    // Jump directly to 2nd element (index 1 -> byte 4)
    int targetIndex = 1;
    infile.seekg(targetIndex * sizeof(int), ios::beg);
 
    cout << "tellg() position: " << infile.tellg() << " bytes." << endl; // 4
 
    int val;
    infile.read((char*)&val, sizeof(int));
    cout << "Value at index 1: " << val << " (Expected: 200)" << endl;
 
    infile.close();
    return 0;
}

Step 8.3: Tabular Formatting via Stream Manipulators

#include <iostream>
#include <iomanip>
#include <cmath>
using namespace std;
 
int main() {
    double values[3] = {2.0, 16.0, 144.0};
 
    // Header
    cout << left << setw(8) << "INDEX" 
         << right << setw(16) << "SQRT_VALUE" << endl;
 
    // Formatting configuration
    cout << setfill('.');
    cout << fixed << setprecision(3);
 
    for (int i = 0; i < 3; i++) {
        // setw(8) applies to index; setw(16) applies to sqrt
        cout << left << setw(8) << (i + 1) 
             << right << setw(16) << sqrt(values[i]) << endl;
    }
 
    return 0;
}

πŸ“Š 4. Quiz Master’s Blueprint (Predicting Output Traps)

Trap 1: The setw-setfill Reversion Trap

cout << setw(8) << setfill('&') << 90 << 1;
  1. setfill('&') sets padding character to & permanently.
  2. setw(8) sets temporary field width of 8 columns for 90 only.
  3. 90 is right-justified: 6 padding & + 90 &&&&&&90.
  4. setw(8) immediately deactivates (reverts to width 0).
  5. 1 is printed with zero padding.
  • Final Output: &&&&&&901 (NOT &&&&&&90&&&&&&1).

Trap 2: Default setprecision vs. fixed

double val = 123.4567;
cout << setprecision(4) << val << " | " << fixed << setprecision(2) << val;
  • Default setprecision(4): 4 total significant digits 123.5.
  • fixed << setprecision(2): 2 fractional digits after decimal 123.46.
  • Final Output: 123.5 | 123.46

πŸ’¬ 5. Viva Quick-Prep

Q1: What is the difference between seekg() and seekp()?

Answer: seekg() (seek get) moves the read-pointer inside input streams (ifstream), while seekp() (seek put) moves the write-pointer inside output streams (ofstream).

Q2: Why does ifstream::read() require a cast to (char*)?

Answer: Because binary stream functions are designed to operate on raw byte arrays typed as char*. Passing any other variable type requires casting its address ((char*)&var).

Q3: How do we change output alignment in setw() from right to left?

Answer: Insert the stream manipulator std::left (e.g., cout << left << setw(10) << "Data";).