optional · CSS study notes
Files, Streams, and Exception Handling (extended, incl. Random Access Files)
- Sequential access files: data read/written in order from beginning to end
- Random access files: allows jumping directly to any position (e.g., via
seekg()/seekp()in C++) without reading through preceding data - File processing modes: read, write, append, binary vs text mode
- Exception Handling:
tryblock (code that may throw an error) —catchblock (handles the specific exception type) —throw(raises an exception) — enables graceful error recovery instead of abrupt program termination
File Open Mode Flags (fstream) — commonly tested
| Flag | Meaning |
|---|---|
ios::in | Open for reading |
ios::out | Open for writing |
ios::app | Append — writes go to the end, existing content preserved |
ios::ate | Open and immediately seek to the end (unlike app, you CAN seek elsewhere afterward) |
ios::trunc | Discard existing content on open (default with ios::out) |
ios::binary | Open in binary mode — required for raw struct/class I/O (.write/.read) |
Sequential file write + read example
#include <fstream>
#include <iostream>
using namespace std;
int main()
{
ofstream fout("data.txt");
fout << "Hello CSS aspirant" << endl;
fout.close();
ifstream fin("data.txt");
string line;
getline(fin, line);
cout << "Read back: " << line << endl;
fin.close();
return 0;
}
Exception handling example
#include <iostream>
using namespace std;
int divide(int a, int b)
{
if (b == 0)
throw runtime_error("Division by zero!"); // throw
return a / b;
}
int main()
{
try // try block
{
cout << divide(10, 0) << endl;
}
catch (const runtime_error& e) // catch block
{
cout << "Caught exception: " << e.what() << endl;
}
return 0;
}
Output: Caught exception: Division by zero! — program continues instead of crashing.
Self-Test Questions
- What's the difference between
ios::appandios::ate? - Why must
ios::binarybe used when writing raw structs/classes to a file? - What happens to program flow when an exception is thrown but no matching
catchexists?
Random Access Files — worked example (seekg / seekp)
#include <fstream>
#include <iostream>
using namespace std;
struct Record { int id; char name[10]; };
int main()
{
// Write 3 fixed-size records
ofstream fout("records.dat", ios::binary);
Record r1 = {1, "Ali"}, r2 = {2, "Sara"}, r3 = {3, "Bilal"};
fout.write((char*)&r1, sizeof(Record));
fout.write((char*)&r2, sizeof(Record));
fout.write((char*)&r3, sizeof(Record));
fout.close();
// Jump DIRECTLY to the 2nd record without reading the 1st
fstream file("records.dat", ios::in | ios::out | ios::binary);
file.seekg(1 * sizeof(Record), ios::beg); // skip straight to record index 1
Record r;
file.read((char*)&r, sizeof(Record));
cout << "Record 2: " << r.id << " " << r.name << endl;
// Overwrite that same record in place
r.id = 99;
file.seekp(1 * sizeof(Record), ios::beg); // move write pointer back to the same spot
file.write((char*)&r, sizeof(Record));
file.close();
return 0;
}
Output: Record 2: 2 Sara
Key exam point: seekg() moves the read pointer, seekp() moves the write pointer — in a fstream opened for both, they're often used together, as shown here: read a record, modify it, then seek back to overwrite just that one record. This only works because every Record is a fixed size, so n * sizeof(Record) always lands exactly on record n's starting byte — the defining advantage of random access over sequential access.