Chapter 20: File I/O & Serialization
File I/O & Serialization in C++: Reading, Writing, and Saving Your Data Like a Pro!
Hello my wonderful student! π Welcome to Lesson 20 β File I/O & Serialization β the chapter where your programs stop living only in memory and start interacting with the real world β saving data to files, reading configuration, loading game saves, exporting reports, and much more!
File I/O is one of the most practical skills youβll learn β almost every real application needs it.
Today weβll cover everything in great detail:
- Reading & writing files using <fstream> (ifstream, ofstream, fstream)
- Text mode vs Binary mode β when to use which
- Serialization β saving and loading structured data
- Popular modern libraries: nlohmann/json (JSON) & tinyxml2 (XML)
Weβll go very slowly, with real-life analogies, tons of complete examples, common mistakes, and best practices.
Letβs start!
1. The Basics: <fstream> β The Standard Way to Work with Files
Include: #include <fstream> Main classes:
- std::ifstream β input (reading)
- std::ofstream β output (writing)
- std::fstream β both reading & writing
Modes:
| Mode | Meaning | Typical use |
|---|---|---|
| std::ios::in | Open for reading | ifstream default |
| std::ios::out | Open for writing (truncates file) | ofstream default |
| std::ios::app | Append (writes at the end) | Logs |
| std::ios::binary | Binary mode (no newline translation) | Images, objects |
| std::ios::ate | Open and move to end immediately | Random access |
Simple text file reading & writing example
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
#include <iostream> #include <fstream> #include <string> int main() { // 1. Writing to a file (text mode) std::ofstream outFile("output.txt"); if (!outFile.is_open()) { std::cerr << "Failed to open output.txt for writing!\n"; return 1; } outFile << "Hello from C++!\n"; outFile << "This is line 2.\n"; outFile << 42 << " is the answer.\n"; outFile.close(); // good practice, though destructor closes it // 2. Reading from the file std::ifstream inFile("output.txt"); if (!inFile.is_open()) { std::cerr << "Failed to open output.txt for reading!\n"; return 1; } std::string line; while (std::getline(inFile, line)) { std::cout << "Read: " << line << "\n"; } inFile.close(); return 0; } |
Output file (output.txt):
|
0 1 2 3 4 5 6 7 8 |
Hello from C++! This is line 2. 42 is the answer. |
Important safety tips:
- Always checkis_open() or use if (outFile)
- Use close() explicitly when youβre done (though destructor does it)
- Prefer RAII style β let scope handle closing
2. Text Mode vs Binary Mode β Very Important Difference!
Text mode (default):
- Translates newlines (\n) β platform-specific (\r\n on Windows)
- Good for human-readable files (.txt, .csv, .log)
Binary mode:
- No translation β exact bytes
- Required for images, executables, serialized objects
Example: Writing & Reading Binary Data
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
#include <iostream> #include <fstream> struct Player { int id; double health; char name[32]; }; int main() { // Writing binary std::ofstream binOut("player.dat", std::ios::binary); if (!binOut) { std::cerr << "Cannot open player.dat for writing\n"; return 1; } Player p{42, 99.5, "Webliance"}; binOut.write(reinterpret_cast<const char*>(&p), sizeof(Player)); binOut.close(); // Reading binary std::ifstream binIn("player.dat", std::ios::binary); if (!binIn) { std::cerr << "Cannot open player.dat for reading\n"; return 1; } Player loaded; binIn.read(reinterpret_cast<char*>(&loaded), sizeof(Player)); std::cout << "Loaded player:\n"; std::cout << "ID: " << loaded.id << "\n"; std::cout << "Health: " << loaded.health << "\n"; std::cout << "Name: " << loaded.name << "\n"; return 0; } |
Warning: Binary serialization with raw write/read is not portable (endianness, padding, struct layout can differ between compilers/machines).
Better modern way: Use JSON or a proper serialization library (next section).
3. Modern Serialization: JSON with nlohmann/json (Most Popular Choice)
nlohmann/json (also called json.hpp) is the gold standard for JSON in C++ β single-header, easy, powerful.
How to get it: Download from: https://github.com/nlohmann/json Include: #include “json.hpp” (or use package manager)
Example: Saving & Loading a Game Save in JSON
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
#include <iostream> #include <fstream> #include "json.hpp" // nlohmann/json using json = nlohmann::json; int main() { // 1. Create data json gameSave = { {"player", { {"name", "Webliance"}, {"level", 15}, {"health", 92.5}, {"inventory", {"Sword", "Shield", "Potion"}} }}, {"score", 12500}, {"timestamp", "2026-01-22"} }; // 2. Write to file (pretty print) std::ofstream out("gamesave.json"); out << std::setw(4) << gameSave << std::endl; // nice formatting out.close(); // 3. Read back std::ifstream in("gamesave.json"); json loaded; in >> loaded; // 4. Access like normal objects std::cout << "Player name: " << loaded["player"]["name"] << "\n"; std::cout << "Level: " << loaded["player"]["level"] << "\n"; std::cout << "Health: " << loaded["player"]["health"] << "\n"; std::cout << "Inventory:\n"; for (const auto& item : loaded["player"]["inventory"]) { std::cout << " - " << item << "\n"; } return 0; } |
Output file (gamesave.json β beautifully formatted):
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
{ "player": { "name": "Webliance", "level": 15, "health": 92.5, "inventory": [ "Sword", "Shield", "Potion" ] }, "score": 12500, "timestamp": "2026-01-22" } |
Advantages of JSON:
- Human-readable & editable
- Portable across platforms
- Supported in almost every language
- Easy error handling with json::parse()
4. XML with tinyxml2 β When You Need XML
tinyxml2 is small, fast, and simple.
Example: Reading & Writing XML
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
#include <iostream> #include "tinyxml2.h" using namespace tinyxml2; int main() { XMLDocument doc; // Create structure XMLElement* root = doc.NewElement("GameSave"); doc.InsertFirstChild(root); XMLElement* player = doc.NewElement("Player"); player->SetAttribute("name", "Webliance"); player->SetAttribute("level", 15); root->InsertEndChild(player); XMLElement* inventory = doc.NewElement("Inventory"); inventory->InsertEndChild(doc.NewElement("Item")->SetText("Sword")); inventory->InsertEndChild(doc.NewElement("Item")->SetText("Potion")); player->InsertEndChild(inventory); // Save to file doc.SaveFile("gamesave.xml"); // Load & read doc.LoadFile("gamesave.xml"); if (doc.Error()) { std::cerr << "Error loading XML: " << doc.ErrorStr() << "\n"; return 1; } XMLElement* loadedPlayer = doc.FirstChildElement("GameSave")->FirstChildElement("Player"); std::cout << "Name: " << loadedPlayer->Attribute("name") << "\n"; std::cout << "Level: " << loadedPlayer->IntAttribute("level") << "\n"; return 0; } |
When to use XML vs JSON:
- JSON β almost always (simpler, faster, more popular)
- XML β legacy systems, some enterprise software, when attributes are important
5. Best Practices & Common Mistakes
| Best Practice | Reason / Avoids |
|---|---|
| Always check is_open() or use if (file) | File might not exist / permission denied |
| Use std::filesystem (C++17+) for paths | Safer path handling |
| Prefer JSON for modern apps | Easy, portable, human-readable |
| Use std::string for reading lines | Avoid fixed-size buffers |
| Never mix text & binary on same file | Corruption risk |
| Use RAII β let scope close files | No leaks on exceptions |
Your Mini Homework (Try These!)
- Write a program that reads a text file line by line and counts how many lines contain the word “C++”.
- Save a list of 5 students (name, roll, marks) to a JSON file using nlohmann/json.
- Create a function that loads the JSON file and prints students with marks > 80.
- Try writing and reading a binary file containing a struct with strings (hint: youβll need to handle string length manually or use JSON instead).
Youβre doing absolutely phenomenal! Youβve just learned how to make your programs persistent β saving and loading real data β this is what turns toy programs into real applications!
Next lesson: Project Time β weβll build a complete mini-project (like a simple Student Management System) using everything weβve learned!
Any questions? Confused about binary vs text? Want more examples with JSON or XML? Just ask β your friendly C++ teacher is right here for you! π
