4.4 File I/O — Modes, Methods, Pointers & Random Access
File I/O in C++
File operations use the <fstream> header and three classes:
| Class | Direction | Header |
|---|---|---|
ofstream | Output to file (write) | <fstream> |
ifstream | Input from file (read) | <fstream> |
fstream | Both directions | <fstream> |
#include <fstream>
using namespace std;
---
Opening and Closing Files
Two ways to open
// Method 1: constructor
ofstream fout("output.txt");
// Method 2: open() method
ofstream fout;
fout.open("output.txt");
Always close
fout.close();
If you don't close, the destructor will close — but explicit close() is cleaner and lets you check for errors immediately.
Checking if open succeeded
ofstream fout("output.txt");
if (!fout) { // or: if (!fout.is_open())
cout << "Cannot open file";
return 1;
}
---
File Open Modes
| Mode | Constant | Description |
|---|---|---|
in | ios::in | Open for reading (default for ifstream) |
out | ios::out | Open for writing (default for ofstream) |
app | ios::app | Append — write at end |
ate | ios::ate | At-end — start position at end, but allow random write |
trunc | ios::trunc | Truncate to zero length if exists |
binary | ios::binary | Binary mode (no text translation) |
Modes can be combined with | (bitwise OR):
ofstream fout("data.bin", ios::out | ios::binary | ios::app);
fstream ff("file.txt", ios::in | ios::out);
Common combinations
| Use case | Mode | |
|---|---|---|
| Read text file | ifstream f("file.txt"); (default in) | |
| Write new text file (overwrite) | ofstream f("file.txt"); (default `out | trunc`) |
| Append to text file | ofstream f("file.txt", ios::app); | |
| Read + write text | `fstream f("file.txt", ios::in | ios::out);` |
| Read binary | ifstream f("file.bin", ios::binary); | |
| Write binary | ofstream f("file.bin", ios::binary); |
---
Writing to a Text File
#include <fstream>
using namespace std;
int main() {
ofstream fout("students.txt");
if (!fout) {
cout << "Cannot open file";
return 1;
}
fout << "Rohit Jangra" << " " << 101 << " " << 88.5 << "\n";
fout << "Priya Sharma" << " " << 102 << " " << 92.3 << "\n";
fout << "Amit Kumar" << " " << 103 << " " << 75.0 << "\n";
fout.close();
cout << "Data written";
return 0;
}
Reading from a Text File
#include <fstream>
#include <iostream>
using namespace std;
int main() {
ifstream fin("students.txt");
if (!fin) {
cout << "Cannot open file";
return 1;
}
string name1, name2;
int id;
double marks;
while (fin >> name1 >> name2 >> id >> marks) { // assuming "First Last id marks"
cout << name1 << " " << name2 << " " << id << " " << marks << "\n";
}
fin.close();
return 0;
}
Reading Line-by-Line
ifstream fin("data.txt");
string line;
while (getline(fin, line)) {
cout << line << endl;
}
---
Binary File I/O
For non-text data — structs, integers, raw bytes — use binary mode with write() / read():
struct Student {
int id;
char name[50];
float marks;
};
int main() {
Student s1 = {101, "Rohit", 88.5};
// Write
ofstream fout("students.dat", ios::binary);
fout.write(reinterpret_cast<char*>(&s1), sizeof(s1));
fout.close();
// Read
Student s2;
ifstream fin("students.dat", ios::binary);
fin.read(reinterpret_cast<char*>(&s2), sizeof(s2));
cout << s2.id << " " << s2.name << " " << s2.marks;
fin.close();
return 0;
}
Binary mode preserves bytes exactly. Text mode may translate\n↔\r\non Windows. For non-text, always use binary.
---
File Pointers and Random Access
Streams maintain two pointers:
| Pointer | Purpose |
|---|---|
get pointer (tellg/seekg) | Position for reading |
put pointer (tellp/seekp) | Position for writing |
Get pointer functions
| Function | Purpose |
|---|---|
fin.tellg() | Get current read position |
fin.seekg(pos) | Set absolute read position |
fin.seekg(offset, direction) | Set relative read position |
Put pointer functions
| Function | Purpose |
|---|---|
fout.tellp() | Get current write position |
fout.seekp(pos) | Set absolute write position |
fout.seekp(offset, direction) | Set relative write position |
Seek directions
| Constant | From |
|---|---|
ios::beg | Beginning of file |
ios::cur | Current position |
ios::end | End of file |
Examples
fin.seekg(0, ios::beg); // rewind to beginning
fin.seekg(0, ios::end); // jump to end
int size = fin.tellg(); // get file size
fin.seekg(100); // jump to byte 100
fin.seekg(-50, ios::end); // jump to 50 bytes before end
fin.seekg(20, ios::cur); // skip 20 bytes ahead
---
Random Access — Reading the Nth Record
For fixed-size records, random access is direct:
struct Student {
int id;
char name[50];
float marks;
};
int main() {
fstream fs("students.dat", ios::in | ios::out | ios::binary);
// Read student #5 (zero-indexed)
int recordNum = 4;
fs.seekg(recordNum * sizeof(Student));
Student s;
fs.read(reinterpret_cast<char*>(&s), sizeof(s));
cout << "Student 5: " << s.id << " " << s.name << endl;
// Update student #5's marks
s.marks = 95.0;
fs.seekp(recordNum * sizeof(Student));
fs.write(reinterpret_cast<char*>(&s), sizeof(s));
fs.close();
return 0;
}
This is the classic fixed-length-record database pattern — used in legacy systems, embedded systems, and high-performance I/O.
---
Sequential vs Random Access
| Aspect | Sequential Access | Random Access |
|---|---|---|
| Reading order | Beginning to end | Any position |
| Best for | Logs, text streams | Databases, indexed files |
| File format | Variable-length lines | Fixed-length records |
| Position knowledge | Not needed | Required |
| Functions | >>, getline, read | seekg, seekp + read/write |
---
File State Checking
| Function | Returns true if |
|---|---|
fin.good() | No errors |
fin.eof() | End of file reached |
fin.fail() | Format error or EOF |
fin.bad() | Stream corrupted |
fin.is_open() | File is open |
if (fin) | Not fail and not bad |
ifstream fin("nofile.txt");
if (!fin.is_open()) {
cout << "File not opened";
}
---
Error Handling During File Operations
ifstream fin("data.txt");
if (!fin) {
cout << "Error: cannot open input file";
return 1;
}
// Try reading; check for failure
int x;
fin >> x;
if (fin.fail()) {
if (fin.eof()) cout << "Reached end of file";
else cout << "Read error";
}
Modern: use exceptions
ifstream fin("data.txt");
fin.exceptions(ifstream::failbit | ifstream::badbit);
try {
int x;
fin >> x;
}
catch (const ifstream::failure& e) {
cout << "I/O error: " << e.what();
}
---
Persistent Objects
A persistent object is one whose state survives beyond a program run — saved to disk and re-loaded.
class Account {
int id;
char name[50];
double balance;
public:
Account() = default;
Account(int i, const char* n, double b) : id(i), balance(b) {
strncpy(name, n, 50);
}
// Save object to file
void save(ofstream& fout) {
fout.write(reinterpret_cast<char*>(this), sizeof(Account));
}
// Load object from file
void load(ifstream& fin) {
fin.read(reinterpret_cast<char*>(this), sizeof(Account));
}
void display() {
cout << id << " " << name << " ₹" << balance << endl;
}
};
int main() {
Account a(1, "Rohit", 50000);
ofstream fout("accounts.dat", ios::binary);
a.save(fout);
fout.close();
Account b;
ifstream fin("accounts.dat", ios::binary);
b.load(fin);
fin.close();
b.display(); // 1 Rohit ₹50000
return 0;
}
Limitations of direct serialisation:
- Works only for POD types (no pointers, no virtual functions, no
string) - Not portable (endianness, struct padding)
- For real applications, use JSON / XML / Protocol Buffers / SQLite
---
Command Line Arguments
C++ programs receive command-line arguments via main's parameters:
int main(int argc, char* argv[]) {
cout << "Program name: " << argv[0] << endl;
cout << "Argument count: " << argc << endl;
for (int i = 1; i < argc; i++) {
cout << "Arg " << i << ": " << argv[i] << endl;
}
return 0;
}
$ ./program hello world 42
Program name: ./program
Argument count: 4
Arg 1: hello
Arg 2: world
Arg 3: 42
Standard parameters:
argc(argument count) — number of arguments including program nameargv(argument vector) — array of C-style stringsargv[0]is the program name
Use case — file copy
int main(int argc, char* argv[]) {
if (argc != 3) {
cout << "Usage: " << argv[0] << " <source> <dest>" << endl;
return 1;
}
ifstream src(argv[1], ios::binary);
ofstream dst(argv[2], ios::binary);
if (!src || !dst) {
cout << "Cannot open files";
return 1;
}
dst << src.rdbuf(); // efficient one-line copy
cout << "Copied " << argv[1] << " to " << argv[2];
return 0;
}
$ ./copy input.txt output.txt
---
Complete Example — Student Records Database
#include <iostream>
#include <fstream>
#include <cstring>
using namespace std;
struct Student {
int id;
char name[50];
float marks;
};
void writeStudents(const string& filename, Student arr[], int n) {
ofstream fout(filename, ios::binary);
if (!fout) { cout << "Write failed"; return; }
fout.write(reinterpret_cast<char*>(arr), n * sizeof(Student));
fout.close();
}
void readAllStudents(const string& filename) {
ifstream fin(filename, ios::binary);
if (!fin) { cout << "Read failed"; return; }
Student s;
while (fin.read(reinterpret_cast<char*>(&s), sizeof(s))) {
cout << s.id << " " << s.name << " " << s.marks << endl;
}
fin.close();
}
Student readNthStudent(const string& filename, int n) {
ifstream fin(filename, ios::binary);
fin.seekg(n * sizeof(Student)); // random access
Student s;
fin.read(reinterpret_cast<char*>(&s), sizeof(s));
fin.close();
return s;
}
void updateNthStudent(const string& filename, int n, const Student& s) {
fstream fs(filename, ios::in | ios::out | ios::binary);
fs.seekp(n * sizeof(Student));
fs.write(reinterpret_cast<const char*>(&s), sizeof(s));
fs.close();
}
int main() {
Student arr[3] = {
{101, "Rohit", 88.5f},
{102, "Priya", 92.3f},
{103, "Amit", 75.0f}
};
writeStudents("students.dat", arr, 3);
readAllStudents("students.dat");
cout << "---" << endl;
Student s = readNthStudent("students.dat", 1); // student #2
cout << "Student 2: " << s.id << " " << s.name << endl;
s.marks = 99.9f;
updateNthStudent("students.dat", 1, s);
readAllStudents("students.dat");
return 0;
}
---
Study deep
- Text vs binary mode matters on Windows. Text mode translates
\nto\r\non write and back on read. Use binary for non-text data, or to preserve exact bytes.
- **
reinterpret_cast<char>(&obj)is the standard idiom for binary I/O.* Treats any object as a byte array. Safe for POD types; unsafe for objects with pointers / virtual functions.
- File pointers
tellg/tellpmay differ.fstreamopened in read+write mode has separate get and put positions — they can diverge.
- Modern serialisation libraries. For real-world apps, use JSON (nlohmann/json), XML (pugixml), Protocol Buffers, MessagePack instead of raw binary. They handle versioning, types, portability.
std::filesystem(C++17). Modern file/path operations:
#include <filesystem>
namespace fs = std::filesystem;
fs::path p("data.txt");
if (fs::exists(p)) cout << "Size: " << fs::file_size(p);
Key Terms — Lesson 4.4
The terms below cover C++ file I/O — every PYQ on file streams, modes, pointers, or random access expects fluent use.
<fstream> Header — The C++ standard header that declares the file stream classes — ifstream (input), ofstream (output), fstream (both). Required for any file I/O the C++ way.
ifstream — The input file stream class — for reading from a file. Constructor opens the file; destructor closes it (RAII). Derives from istream, so it inherits all the >>, getline, get, read operations.
ofstream — The output file stream class — for writing to a file. Constructor opens (creating or truncating); destructor closes. Derives from ostream, inheriting <<, put, write.
fstream — The bidirectional file stream — for both reading and writing the same file. Derives from iostream. Useful for in-place file updates and random-access patterns.
File Open Mode — A bitmask flag passed to a file stream's constructor or open() controlling how the file is opened. Combined with |. Common modes: ios::in (read), ios::out (write), ios::app (append), ios::ate (open and seek to end), ios::trunc (truncate if exists), ios::binary (binary mode).
ios::in — Open for input (reading). Default for ifstream. File must exist; otherwise the stream's fail bit is set.
ios::out — Open for output (writing). Default for ofstream. File is created if missing and truncated (emptied) if it exists, unless ios::app or ios::ate is also specified.
ios::app (Append Mode) — Open for output with appending only — every write goes to the current end of file, regardless of seek position. Existing content is preserved.
ios::ate ("at end") — Open and immediately seek to the end of the file, but subsequent writes can be at any position the program seeks to. Different from app (which forces every write to end).
ios::trunc — Open for output and discard existing contents. Default behaviour for ofstream when not specified. Combined with ios::out for explicit truncation.
ios::binary (Binary Mode) — Open in binary mode — no text translation (no \n ↔ \r\n conversion on Windows). Essential for non-text data (images, serialised structs); recommended for cross-platform reliability even for text where exact byte preservation matters.
Text Mode vs Binary Mode — Two file-open modes with subtle but important differences. Text mode (default) performs line-ending translation on Windows (\n ↔ \r\n). Binary mode transmits bytes exactly as written/read. Use binary for non-text data and for cross-platform compatibility.
open() Method — A file-stream method that opens a file after the stream is constructed: ifstream fin; fin.open("data.txt");. Equivalent to passing the filename to the constructor. Useful for reusing a stream variable across multiple files.
close() Method — A file-stream method that closes the file, flushing buffers and releasing the OS file handle. Called automatically by the destructor (RAII), so explicit close() is rarely needed — but useful when reopening the same stream for a different file.
is_open() Method — A method that returns true if the file was opened successfully, false otherwise. Standard pattern: if (!fin.is_open()) { / error handling / }.
File Pointer / Position — The current read/write position inside the file, measured in bytes from the beginning. Every stream tracks its position; reading or writing advances it.
Get Pointer vs Put Pointer — Input streams have a get pointer (read position). Output streams have a put pointer (write position). fstream opened for both has both pointers independently.
seekg() / seekp() — Methods that move the get / put pointer to a specified position. seekg(pos) moves to absolute byte position; seekg(offset, direction) moves relative to ios::beg (beginning), ios::cur (current), or ios::end (end of file).
tellg() / tellp() — Methods that return the current get / put pointer position as a streampos. Useful for recording the position to seek back to later.
Random Access — The ability to read or write any position in the file in any order, not just sequentially. Achieved with seekg/seekp to position the pointer and read/write to perform the operation. Required for fixed-record-size databases where you compute byte offsets and jump directly.
Fixed-Length Record — A binary file layout where each record occupies the same number of bytes, allowing direct access to the N-th record at offset N × recordSize. The pre-database approach to flat-file storage; still useful for embedded systems and high-performance binary files.
read() Method — A method that reads up to N raw bytes from a stream into a buffer: fin.read(buffer, N); or fin.read(reinterpret_cast<char*>(&obj), sizeof(obj));. The byte-level counterpart of >>. Returns the stream; fin.gcount() reports how many bytes were actually read.
write() Method — A method that writes N raw bytes from a buffer to a stream: fout.write(buffer, N); or fout.write(reinterpret_cast<char*>(&obj), sizeof(obj));. The byte-level counterpart of <<.
Binary I/O Idiom — The standard pattern for reading/writing structs in binary form: fout.write(reinterpret_cast<char*>(&obj), sizeof(obj));. Safe for POD (Plain Old Data) types; unsafe for objects containing pointers, virtual functions, or non-trivial members (their addresses don't survive serialisation).
POD (Plain Old Data) — A type that has the same memory layout as a C struct — no virtual functions, no constructors that initialise members non-trivially, no inheritance. POD types can be safely serialised with write/read of raw bytes. Modern C++ replaces POD with the more nuanced concept of trivially-copyable types.
End of File (EOF) — The condition reached when all data in the file has been read. Indicated by the stream's eof flag. The idiomatic read loop: while (fin >> value) exits cleanly when EOF or read error occurs.
Command Line Arguments — Parameters passed to a program at invocation, received by main(int argc, char* argv[]). argc is the count (always ≥ 1); argv is the array of C-strings, with argv[0] being the program name and argv[1..argc-1] the user-supplied arguments.
rdbuf() — A method that returns the stream's underlying stream buffer. dst << src.rdbuf(); is the fastest way to copy one stream to another because it bypasses character-by-character processing.
std::filesystem (C++17) — The modern standard library for file and directory operations beyond simple I/O — path manipulation, existence/size queries, directory iteration, file copying and removal. #include <filesystem> and std::filesystem::exists(p) / file_size(p) / copy(src, dst).
Serialisation — The process of converting in-memory objects to a sequence of bytes for storage or transmission. Binary file I/O is the most direct form; modern serialisation libraries (nlohmann/json, Protocol Buffers, MessagePack, Cap'n Proto) handle versioning, types, and cross-platform compatibility.
File Stream Failure Modes — Common errors: file does not exist (open fails for input), permission denied, disk full, broken pipe. Always check is_open() after opening; check the stream's truthiness after each operation; handle failures explicitly rather than assuming success.
---
PYQ pattern (very common): "Write a C++ program to read from and write to a file." — Useifstream/ofstream; show open, check open, write/read with<</>>, close; mention modes.
PYQ pattern: "Explain file pointers and random access in C++ files." — Two pointers (get + put);seekg/seekpwith positions and directions; example of accessing N-th record.
PYQ pattern: "What are command line arguments? Explain with example." —int main(int argc, char* argv[]);argc= count,argv= array of strings;argv[0]= program name; example file-copy program.
PYQ pattern: "Explain file open modes in C++." — Table 6 modes (in, out, app, ate, trunc, binary); show combinations with |; common use cases per mode.