optional · CSS study notes
21. File Processing
Covers the modes and techniques for reading, writing, and modifying files:
| Mode | Use case | |
|---|---|---|
Read (ios::in) | Loading existing data | |
Write (ios::out • ios::trunc) | Creating a fresh file, discarding old content | |
Append (ios::app) | Adding new records without disturbing existing ones | |
Binary (ios::binary) | Required for raw struct/class I/O and for seekg/seekp positional access | |
| Read + Write (`ios::in \ | ios::out`) | Needed for in-place updates (read a record, then overwrite it) |
fstream file("students.dat", ios::in | ios::out | ios::binary);
// combining flags with | lets one stream object both read AND write the same file
See the Random Access Files page for a full in-place update example using this combination.
Full lifecycle example — combining modes for a real update workflow
#include <fstream>
#include <iostream>
using namespace std;
struct Product { int id; char name[20]; double price; };
int main()
{
// 1. WRITE mode -- create the file fresh
ofstream fout("products.dat", ios::out | ios::binary | ios::trunc);
Product p1 = {1, "Pen", 2.5}, p2 = {2, "Book", 15.0};
fout.write((char*)&p1, sizeof(Product));
fout.write((char*)&p2, sizeof(Product));
fout.close();
// 2. APPEND mode -- add a new record without touching existing ones
ofstream fapp("products.dat", ios::app | ios::binary);
Product p3 = {3, "Notebook", 8.0};
fapp.write((char*)&p3, sizeof(Product));
fapp.close();
// 3. READ + WRITE mode -- update an existing record in place
fstream fedit("products.dat", ios::in | ios::out | ios::binary);
fedit.seekp(0 * sizeof(Product), ios::beg); // go to record 0
p1.price = 3.0; // updated price
fedit.write((char*)&p1, sizeof(Product));
fedit.close();
return 0;
}
Choosing the right mode — decision guide
| Goal | Mode(s) to use |
|---|---|
| Start completely fresh, losing old data | ios::out (implies ios::trunc) |
| Add new data, keep everything old | ios::app |
| Update specific existing records | `ios::in \ |
| Store/retrieve raw objects, not text | always add ios::binary |
Important MCQs
- To update a specific existing record in place, you need to open the file with:
- a)
ios::apponly b)ios::outonly c)ios::incombined withios::outd)ios::trunconly
- a)
- Using
ios::outalone (withoutios::app) when opening an existing file will:- a) Append to it b) Truncate/discard its existing content c) Refuse to open d) Convert it to binary
- Which flag is essential when reading/writing raw struct/class objects rather than text?
- a)
ios::appb)ios::truncc)ios::binaryd)ios::ate
- a)
Self-Test Questions
- Why does step 1 in the example use
ios::truncexplicitly? - What would happen if step 2 used
ios::outinstead ofios::app? - Why does updating a specific record (step 3) require both
ios::inandios::outtogether?
📌 Sample & Repeated FPSC Questions (2016–2026)
2020, Paper I, Q5(b) (15 marks) — Write C++ programs to compute following: i. Add a student ii. Add a course iii. Find a student with respect to CGPA iv. Add a Teacher v. Update a student. (Full solution: Classes and Objects — Exam Practice → "University Records System" page.)
This is FPSC's best real illustration of "choosing the right mode per operation" — the exact decision this page's guide above walks through, but applied across three separate files at once:
- Add a student / course / teacher →
students.dat/courses.dat/teachers.dat, each openedios::app | ios::binary— a brand-new record only, existing ones must survive. - Find a student by CGPA →
students.dat, opened read-only (ifstream,ios::binary) — pure scanning, nothing is modified. - Update a student →
students.dat, openedios::in | ios::out | ios::binary— must locate an existing record AND overwrite it in place, so both directions are needed on one stream object.
Exam angle: a 15-mark "design and persist a system" question like this is really three or four separate file-processing sub-questions in disguise — grade each operation independently on whether it opens its file with the correct combination of modes, not just on whether the code compiles.