Video Coming Soon...
20: Input From a File
In this exercise you will learn a simple way to read data from a file. C++ has another way to read from a file, but that way involves very complicated topics like Object Oriented Programming. In fact, to really understand C++'s I/O you would need a Ph.D. in everything C++ has and probably a couple in Physics and a masters in Mechanical Engineering.
For this exercise we will learn to do I/O in the Grug Brained style we've been using this whole time. You open file. You read line from file. You do thing to line. For many tasks that's all you need, and if you need more then you can start working on your Ph.D. in Object Oriented Template Friend Function Overloading with an Emphasis in Quantum Physics.
The Input .csv
For this exercise you will need a .csv file like this:
View Source file ex20.csv Only
12,10,9,4
8,3,2,1
9,9,4,3
Start with this one and you can add more to it later.
Version 1: Get the Data
The first version you'll create opens the ex20.csv file, reads each line, and parses a single line. This is a large piece of code so take your time getting it to work:
View Source file ex20_v1.cpp Only
#include <fmt/core.h>
#include <fstream>
#include <vector>
using std::vector, std::string, std::ifstream, std::ios;
using fmt::println;
int chunk_number(const string& line, size_t start, size_t end) {
string chunk = line.substr(start, end - start);
return stoi(chunk);
}
vector<int> parse_line(const string& line) {
vector<int> result;
size_t start = 0;
size_t end = line.find(",", start);
while(end != string::npos) {
int as_number = chunk_number(line, start, end);
result.push_back(as_number);
start = end + 1;
end = line.find(",", start);
}
// need to get the final chunk
int as_number = chunk_number(line, start, end);
result.push_back(as_number);
return result;
}
int main(int argc, char* argv[]) {
string filename{argv[1]};
ifstream in_file{filename, std::ios::binary};
if(!in_file.is_open()) {
println("failed to open {}", filename);
return 1;
}
string line;
while(in_file) {
getline(in_file, line);
if(line == "") break;
vector<int> row = parse_line(line);
for(int cell : row) {
println("number: {}", cell);
}
}
}
The Breakdown
This breakdown is long, so make sure you take the time to understand each part and what it does. If you still have no idea how this code works after the Breakdown then consider doing a Recreate on it. Write up a description of what it does and try to Recreate it from that. Usually that'll make it stick.
int chunk_number(const string& line, size_t start, size_t end)- You'll learn what
const string&does later, but it's a way to pass a string tochunk_numberwithout copying the whole thing. string chunk = line.substr(start, end - start)- The
string.substr()function extracts a chunk of the string from the start to an end point. return stoi(chunk)- The
stoi()function converts a string to an integer. I read this as "s to i" or "string to int". vector<int> parse_line(const string& line)- This says that
parse_linetakes a string (reference) and returns avectorofints. vector<int> result- We'll put the results of parsing a
.csvline in here. size_t start = 0- Start at zero.
size_t end = line.find(",", start)- Using
string.find()to get the first,(comma) to parse. while(end != string::npos) {- The
string::nposis an indicator that says you're at the end of the string after you callstring.find(). int as_number = chunk_number(line, start, end)- Call
chunk_number()to convert that chunk of the line into a number. result.push_back(as_number)- Store this number in the
resultvector. start = end + 1- Move the
startto the character right after the end, which starts scanning at the next spot in the string. end = line.find(",", start)- Move
endto the next,for the next chunk. int as_number = chunk_number(line, start, end)- The
whileloop has exited, but most (all?).csvusually have a trailing column that doesn't end in a,. That means thestartandendshould be ready for the last column. Do one finalchunk_number()to get it. result.push_back(as_number)- Save the last number.
return result- Return the
vector<int>we made to store the number. int main(int argc, char* argv[])- The main function.
string filename{argv[1]}- We take the 1st argument to
main()and create a string for thefilename. ifstream in_file{filename, std::ios::binary}- This creates an "input file stream" or
ifstreamfor you to read. Theifstreamtakes a filename to open, and additional specifiers to say how to open the file. In this case we needstd::ios::binaryso that Windows doesn't "help" us by converting the file. if(!in_file.is_open()) {- If the
is_open()function returns try then thein_filedidn't actually open. This is an error so report it. println("failed to open {}", filename)- Reporting that we couldn't open the file.
return 1- When you
return 1from aint main()function that signals to the OS that there was an error. string line- This will be the string to store each line of the
.csv. while(in_file)- Even though
in_fileis aifstream, it will behave like aboolwhen you use it like this. This mostly only works inwhile,for, andifstatements. getline(in_file, line)- Get a line of text from
in_fileand put it inline. if(line == "") break- If
lineis empty after that then there's nothing else to read, break out of thewhile(). vector<int> row = parse_line(line)- Call
parse_lineand put the results in avector<int>namedrow. for(int cell : row)- Go through each
cellofrow. println("number: {}", cell)- Print each cell.
Challenge: Vector of Vector Tables
Once you understand this code you can make a small improvement to it. It's possible to create a vector that holds other vector<int>. This would be like your typical Spreadsheet with columns and rows. You create a "vector of vectors" like this:
vector<vector<int>> table;
Use this to change the code so that it completely loads this table and then prints the whole table.
See how I added vector of vectors
View Source file ex20_v1_v2.diff Only--- ex20_v1.cpp 2026-04-17 23:04:31.623332308 -0400
+++ ex20_v2.cpp 2026-04-12 10:36:49.666521626 -0400
@@ -3,7 +3,7 @@
#include <vector>
using std::vector, std::string, std::ifstream, std::ios;
-using fmt::println;
+using fmt::println, fmt::print;
int chunk_number(const string& line, size_t start, size_t end) {
string chunk = line.substr(start, end - start);
@@ -32,7 +32,8 @@
int main(int argc, char* argv[]) {
string filename{argv[1]};
- ifstream in_file{filename, std::ios::binary};
+ ifstream in_file(filename, std::ios::binary);
+ vector<vector<int>> table;
if(!in_file.is_open()) {
println("failed to open {}", filename);
@@ -47,9 +48,14 @@
if(line == "") break;
vector<int> row = parse_line(line);
+ table.push_back(row);
+ }
+ for(vector<int> row : table) {
for(int cell : row) {
- println("number: {}", cell);
+ print("{}\t", cell);
}
+
+ print("\n");
}
}
This is called a diff and it shows the difference between the previous version of the code in ex20_v1.cpp and the new version that I've named ex20_v2.cpp. You read it like this:
- Any red lines that start with
-(minus) are removed. - Any lines that start with
+(plus) are added. - The other lines act as context so you know where these changes are made.
The Practice
It's time to introduce what I call, "The Practice." This is a set of four things that you'll always do at the end of an exercise. These are optional but if you can do them for every exercise then you'll improve your understanding of each one.
- Change It
- Take this code and change it in some way. There's simple changes like changing the text that's output, and more complicated changes like creating functions for everything in
main(). Your purpose here is to understand that you own your code and you are allowed to change it. It also helps you confirm to yourself that you know the code. - Break It
- Try to find novel ways to break this code. It could be with syntax errors, or feeding it bad data. Maybe you craft a malicious
.csvthat causes it to crash. Your purpose here is to understand how your code fails so you can develop an eye for defects. - Recreate It
- Write a description of this code in your own words, then try to recreate it from your description. At first you'll have to refer back to your first version, but eventually you'll be able to recreate it from your description and then from memory. Your purpose here is to build confidence since--if you can do it twice--then it wasn't luck. This also helps you learn how to take descriptions of problems and turn them into working code.
Further Study
Here are the links to everything used in this exercise. Study them:
When you study them, try to spend time studying the examples, the features mentioned, and type of any interesting examples.
Register for Learn C++ the Hard Way
Register to gain access to additional videos which demonstrate each exercise. Videos are priced to cover the cost of hosting.