Video Coming Soon...

Created by Zed A. Shaw Updated 2026-04-30 02:05:52

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 to chunk_number without 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_line takes a string (reference) and returns a vector of ints.
vector<int> result
We'll put the results of parsing a .csv line 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::npos is an indicator that says you're at the end of the string after you call string.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 result vector.
start = end + 1
Move the start to the character right after the end, which starts scanning at the next spot in the string.
end = line.find(",", start)
Move end to the next , for the next chunk.
int as_number = chunk_number(line, start, end)
The while loop has exited, but most (all?) .csv usually have a trailing column that doesn't end in a ,. That means the start and end should be ready for the last column. Do one final chunk_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 the filename.
ifstream in_file{filename, std::ios::binary}
This creates an "input file stream" or ifstream for you to read. The ifstream takes a filename to open, and additional specifiers to say how to open the file. In this case we need std::ios::binary so that Windows doesn't "help" us by converting the file.
if(!in_file.is_open()) {
If the is_open() function returns try then the in_file didn'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 1 from a int 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_file is a ifstream, it will behave like a bool when you use it like this. This mostly only works in while, for, and if statements.
getline(in_file, line)
Get a line of text from in_file and put it in line.
if(line == "") break
If line is empty after that then there's nothing else to read, break out of the while().
vector<int> row = parse_line(line)
Call parse_line and put the results in a vector<int> named row.
for(int cell : row)
Go through each cell of row.
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 vectorsView 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:

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 .csv that 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.

Previous Lesson Next Lesson

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.