Getting Started
If you missed lab or failed to copy my code from the projector during lab session, the files I typed/utilised during lab sections 1 and 2 are available here: [LINK]
Synopsis
You just happen to have a text file that contains the names of a bunch of students and their test scores. Not all of them have taken the same number of tests, however. Some took 4, some took 8, and some took none. How will you know this? You will utilise a stringstream to aid you in getting the data you need to calculate the grades for these students.

Your goal is to use the sstream library in conjunction to the fstream library to read in files containing information about the students, and calculate their grade averages.
What is sstream?
sstream is another library that you can include at the top of your program which will allow you to dissect data from strings more easily. They make conversions between data types fairly easy as well.
You will be introduced to two new data types, known as istringstream and ostringstream (And also stringstream, but be careful with that one).

The istringstream and ostringstream data types serve as follows:
  • istringstream (Input Stringstream) - Lets you feed an entire string into it and use ">>" to read values into variables. The data type of the variables you will read into do not matter. The istringstream will convert them.
  • ostringstream (Output Stringstream) - Lets you make a single string by inputting values via "<<". The variables can be integer, string, char, double, etc. The ostringstream will convert them for you.

Converting a string to an integer via istringstream

Let's say you read in "2" and "3.14" as a single string separated by a space. You wanted to convert the first one to an integer and the second one to a double... for obvious reasons.
#include <iostream>
#include <sstream>

using namespace std;

int main() {
	string values = "2 3.14";
	int val1;
	double val2;

	istringstream ss;
	ss.str(values);

	ss >> val1 >> val2;
}
The result of this is that val1 will contain the number 2, and val2 will contain 3.14. However, they are not strings! They are an int and double respectively. This is because the istringstream converted the values.

Converting list of integers/doubles to a string via ostringstream

In the event that we have a list of integers and wanted to print it out as a list of integers, we can utilise ostringstreams the same way we utilise cout. The difference here is that the output would be stored in a string rather than being printed out. To access this string, you can call "ss.str()", which will give the string inside stringstream ss.
#include <iostream>
#include <sstream>

using namespace std;

const int NUM_OF_VALUE = 5;

int main() {
	double values[] = { 23.6, 85.2, 0.0, 592.52, 23.12 };
	ostringstream ss;
	for (int i = 0; i < NUM_OF_VALUE; i++) {
		ss << values[i] << " ";
	}
	cout << ss.str() << endl;
}
The output of the program is as follows:
23.6 85.2 0 592.52 23.12 

See the pattern?

Hopefully, you have noticed by now how stringstreams, fstream, and cin/cout are related, because they generally do the same thing but are toward different targets each. Here is a list:

Inputs (Use ">>")
  • cin - Accepts input in console.
  • ifstream - Accepts input from a file.
  • istringstream - Accepts input from a string.
Outputs (Use "<<")
  • cout - Prints out to console.
  • ofstream - Prints to a file.
  • ostringstream - "Prints" to a string.
Error Checking
Just like with fstreams, we can error check with stringstreams as well. One of the important checks is to make sure we were even able to read in data. The syntax is pretty much the same as with fstreams. Observe.

Example: Checking how many numbers we read in a line from a file

Let's say you had a file with a bunch of numbers on a single line. Yes, we can use fstream to automate it but what if we don't know how many numbers are on that line? We would have to read in the entire line in one go, and dissect it manually. There is a command to help you with that:
  • getline(ifstream&, string&) - Grabs an entire line from an ifstream (with spaces) and sets a string equal to the entire line.
With this, let's take the following file (example.txt) and calculate the total of all of the numbers on the first line:
10 12 15.5 -2 0.1 16.7
Do the math. The total is 52.3. Now let's program it.
#include <iostream>
#include <fstream>
#include <sstream>

using namespace std;

int main() {
	string line;             //Line to store line in file.
	double total;            //Number to store total
	double value;            //A single value
	ifstream fp;
	fp.open("example.txt");  //Open up file.
	getline(fp, line);       //Read a line from the file "fp" and store it entirely in "line".

	total = 0;
	istringstream ss;
	ss.str(line);            //Set the stringstream's string to "line"
	while (ss >> value) {    //Read in the value to "value", if possible
		total += value;      //Add to the total
	}

	cout << total << endl;   //Print out that total

	fp.close();              //Close the file. Have a nice day.
}
Lost? Okay, let's pay attention to that while loop.
while (ss >> value) {    //Read in the value to "value", if possible
	total += value;      //Add to the total
}
This while loop will constantly read in from the istringstream until it can't anymore. Until then, it will store each value into "value". Inside of that while loop, we add "value" to our "total". The output of the program:
52.3
If you noticed though, it will constantly read until it has nothing more to read! This means that "ss >> value" is able to return true or false depending on if it was able to read values. You can use this to error check whether or not you can read more data or not without reading more data than you should.
Passing Arrays in a function
Usually, if we want to modify a variable in a function, but also change it in its original location outside of the function, we will pass the variable by reference. That may look like this:
void print_string(string& val) {
	cout << val << endl;
}
However, arrays are a different beast. Yes, you can throw arrays into a function and it will gladly take them. The twist? They are always automatically passed by reference. There is nothing you can do about this (yet?). But we can take advantage of this to modify values of arrays in functions and have those arrays changed in other places in our code.

Prototype Syntax

void print_strings(string[]);

Function Declaration

void print_strings(string lines[]) {
	for (int i = 0; i < something; i++) {
		cout << lines[i] << endl;
	}
}
Notice that that example will not compile. To print out a number of lines from a string array, we also need to know its array size. So therefore:
//Prototype
void print_strings(string[], int);

//int main()

//Function Declaration
void print_strings(string lines[], int count) {
	for (int i = 0; i < count; i++) {
		cout << lines[i] << endl;
	}
}

Example: Reversing contents in strings of an array size 3

Let's write a program that will take an array of 3 strings and reverse their contents. No, not "reversing" like what you will do in this lab.... So "Clara Nguyen" will become "neyugN aralC", and etc.
#include <iostream>
#include <string>

using namespace std;

const int STRING_NUM = 3;

void reverse_strings(string[], int);

int main() {
	string lines[STRING_NUM] = {
		"Clara Nguyen",
		"This is a test line",
		"Racecar"
	};
	reverse_strings(lines, STRING_NUM);
	for (int i = 0; i < STRING_NUM; i++) {
		cout << lines[i] << endl;
	}
}

void reverse_strings(string lines[], int count) {
	char temp;
	int length, end;
	for (int i = 0; i < count; i++) {
		length = lines[i].length();
		for (int j = 0; j < length / 2; j++) {
		    temp = lines[i][j];
		    end  = length - j - 1;
		    lines[i][j  ] = lines[i][end];
		    lines[i][end] = temp;
		}
	}
}
The output of this program is:
neyugN aralC
enil tset a si sihT
racecaR
Try it out yourself here! [LINK]
The Lab
The lab is actually simpler than previous labs, though it does build on your knowledge of fstreams and classes as well as your newly found knowledge with stringstreams. The examples I gave above should aid you in doing a majority of this lab.

The lowdown

You are going to have a struct (or class) with 3 public member variables. That's it. No member functions or anything special. Of those 3 variables, 2 of them will be strings for a student's first and last name... followed by a double to hold on to the student's average score. It should be noted that you are working with multiple students here. Therefore, you will store them all in an array, which you will pass into your functions. There are 3 functions you should have:
  • A function that reads in from the file and fills in the array of students.
  • A function that reverses the array. If you don't know what you are doing, do this one last.
  • A function that prints out the contents of the array. You can use this to check if your data is correct
This is a vague list for the functions. Read the lab writeup for full details on it.

I will assume you are good with ifstreams for the remainder of the semester. One tricky part of this lab is utilising that double variable that contains the student's average variable. You can take two approaches to this problem:
  1. You can use that variable to store the total of all of the student's scores while reading in from the istringstream, and keep track of the number of scores read in. Then after that loop, divide by the number of entries read and store it in the same variable.

  2. You can use a temporary variable to store the total, then do the division and store that into the member variable.
After you are able to do that calculation, you are then ready to make the program read in the data for "multiple" students. I'll leave that one to you.