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>:
ifstream: Input file stream (read-only).ofstream: Output file stream (write-only; creates file if non-existent).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/ofstreamclose 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:setwis temporary/one-shot.
File Pointers & Byte Positioning (seekg, tellg)
File streams maintain byte-level index pointers:
getpointer: Tracks reading position {queried bytellg(), moved byseekg()}.putpointer: Tracks writing position {queried bytellp(), moved byseekp()}.
file.seekg(offset, refposition); // Seek get pointer
file.seekp(offset, refposition); // Seek put pointerThe refposition takes one of three constants defined in class ios:
ios::begβ Offset from the start of the file.ios::curβ Offset from the current pointer location.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 exactlypdecimal places. - Persistent: Remains active until modified.
π 2. Keyword & Syntax Dictionary
| Keyword / Concept | Technical Purpose & Syntax | Exam Example |
|---|---|---|
ios::binary | Mode 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;setfill('&')sets padding character to&permanently.setw(8)sets temporary field width of 8 columns for90only.90is right-justified: 6 padding&+90&&&&&&90.setw(8)immediately deactivates (reverts to width 0).1is 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 digits123.5. fixed << setprecision(2): 2 fractional digits after decimal123.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";).