When small pieces of information are needed an input from the user is simple to get, but when large amounts of data need to be processed by a program it gets difficult for a human to enter the data. For this data is often stored in files. A file is a named portion of secondary memory used to permanently store data. In general each line on a file has the information regarding a single entry of data, and can contain as many entries as needed.
In C++ to work with files use a preprocessor directive to include the library:
<fstream>
When working with files a program either must produce output to a file, or read data from the file into the program. So in general there are 2 types of files:
Output files: ofstream
Input files: ifstream
Output files are files that have data written to them by a program. In C++ a variable must be made to hold the output file, which must be of type ofstream (which is a data type from the <fstream> library). The ofstream type allows for a file to be opened into the variable that can be written to only. To use the type a variable of ofstream type can be made with any valid identifier (same rules as variable identifiers):
#include <fstream> using namespace std; int main () { ofstream oFile; return 0; }
The variable oFile can be named with any valid identifier and is ofstream type so an output file will be able to be opened into it and written to in subsequent steps.
Note: Output files can only be written to and not read from, thus they may not be input files at the same time.
Input files are files that contain data to be read by a program. In C++ input files are of the type ifstream (which is a data type from the <fstream> library). Variables of ifstream must contain data that is structured in a way that programs can read, which is discussed in detail as input file discussion progresses. As with output files, the ifstream type can be used to make a variable with any valid identifier:
#include <fstream> using namespace std; int main () { ifstream iFile; return 0; }
The variable iFile can be named with any valid identifier and is ifstream type so an input file can be opened into it and read from in subsequent steps.
Note: Input files can only be read from and not written to, thus they may not be output files at the same time.
Every computer has an undefined amount of files contained on it, and when an input or output file variable it does not know which file to associate with. For this reason input and output file variables must be associated with some file on the computer using:
fstream.open(string)
When the fstream is replaced with any input or output file variable name that has been created and the string is replaced with a valid path/to/a/file then the file at the path will be opened into the input or output file variable to read or written to respectively, which looks like the following:
#include <fstream> using namespace std; int main () { // make the file variables ifstream iFile; ofstream oFile; // open the files iFile.open(“input.txt”); oFile.open(“output.txt”); return 0; }
If the file is located in the directory that the program is located in then the file’s name can simply be provided to the fstream.open() function, otherwise a valid path must be provided to the file. Recall the treelike structure of files in a unix based system:

Everything on the computer starts in the root directory / which has files and directories in it. The directories inside of the root directory create new branches from the root directory which make the structure of the files and directories be a tree-like structure. This makes it easy for every file in the file system to be able to get to all the other files on the computer by just following the tree-like structure to the desired file.
If a file needs to be accessed lower down the tree from the directory the program is in, then use / to navigate through the directories to the file that is desired. So in the above tree to access a file in /downloads/ from a file in / it would be accessed through the path /home/username/downloads/desiredFile.
If a file is needed higher up the tree then use ../ as many times as needed to go back up the tree, until the branch the desired file is on is reached. Then use / to navigate down the branch to the file. So in the above tree to access a file in /tmp/ from a file in /desktop/ it would be accessed through the path ../../../tmp/pathToDesiredFile.
For input files the desired file must be at the path provided or the file will not be able to be opened (which can be checked for in subsequent steps). This is because for a file to be read from it must exist with data in it to be read. This is not the case for the output file though.
When opening output files the file does not necessarily need to be there in order for it to be opened. This is because if the file is not there a new one can be created, which will be empty, so there will be an empty file to output to. This creation of the file happens even if the file is already there as, if the file is already there it may have something in it. So if the file is already there it will be destroyed and recreated when the output file opens the file so that there is a blank file to output to. Thus output files must be opened with extreme caution because a file main.cpp:
#include <iostream> using namespace std; int main () { cout << “My program I worked hard on\n”; }
Could be opened accidentally as an output file, which would leave the file blank (or with any output that the executable program may have produced to it):
Which is a permanent delete that can not be recovered.
Recall writing data to the terminal:
cout << “Hello World\n”;
Once an output file variable is created, and an output file associated with it, data can be written to it in a similar manner:
ofstream << “Hello World\n”;
Where ofstream is replaced with the identifier of any output file variable of ofstream type that has been declared and opened as described above. Now the output following the stream insertion operator << will be placed into the associated file rather than the terminal. Output can still be written to the terminal using cout and the stream insertion operator, just any output statement starting with an ofstream variable will place the output of that statement to the relevant file.
For example, the text ”Hello World” can be output to the file output.txt in the same directory of the program:
#include <fstream> using namespace std; int main () { // output file variable ofstream oFile; // open the output file oFile.open(“output.txt”); // write to the file oFile << “Hello World” << endl; return 0; }
The file output.txt is associated to the output file variable oFile using oFile.open(“output.txt”), which then allows the output Hello World to be output to the file using the stream insertion operator. Thus output.txt will contain the following after the program runs:
Hello World
Variables of ofstream type are output streams just like cout is so all the manipulators from <iomanip> can also be used with output files. So for example if the result of 3.0 / 7.0 needed to be output to a file and limited to 2 decimal places of precision the <iomanip> library could be used as normal:
#include <fstream> #include <iomanip> using namespace std; int main () { // output file variable ofstream oFile; // open the output file oFile.open(“output.txt”); // write to the file oFile << fixed << setprecision(2) << 3.0 / 7.0 << endl; return 0; }
Here all floating-point numbers will be output in fixed-point notation with a precision of 2 decimal places for all floating-point numbers output to oFile. However, if any floating-point numbers were output to the terminal then the numbers on the terminal would not have this formatting since cout did not receive any formatting. Thus output.txt will contain the following after the program runs:
0.43
Recall reading input from the terminal:
int i = 0; cin >> i;
Once an input file variable is created, and an input file associated with it, data can be read from it in a similar manner:
int i = 0; ifstream >> i;
Where ifstream is any input file variable that has been declared and opened using the methods above. The stream extraction operator >> will read from the file up to the first whitespace it sees, then stop at that whitespace and remember where it stopped. When subsequent reads are done from the file the file will pick up where it left off and read until the next whitespace is hit.
So for a file in.txt which contains:
1 2 3 4 5 6 7
Only the 1 would be read from ifstream >> i since there is whitespace directly following the 1. If multiple values are needed from a file at once then multiple reads can be done from the file in a single statement:
int i = 0, j = 0; ifstream >> i >> j;
Now when in.txt is opened into the input file variable ifstream >> i >> j will cause 1 to get placed into i and 2 into j. To get the other values more stream extractions would need to take place.
For example toAdd.txt contains two values that need to be added together:
1.1 2.2
These two values can be read in, added together, and the result output to a file additionResults.txt using the following program:
#include <fstream> using namespace std; int main () { // file variables ifstream addFile; ofstream sumFile; // open the files addFile.open(“toAdd.txt”); sumFile.open(“additionResults.txt”); // read from the file double i = 0, j = 0; addFile >> i >> j; addFile.close(); // file no longer needed // output results sumFile << i << “ + “ << j << “ = “ << i + j << endl; sumFile.close(); // file no longer needed return 0; }
Once the file toAdd.txt is opened into the input file variable addFile it can be read from using the line addFile >> i >> j; which places the value 1.1 into i, since the whitespace causes the first >> to stop reading, and the value 2.2 into j. The two values are then added together and the result output to additionResults.txt which is associated with the output file variable sumFile after the open statement. This causes the following to be output to additionResults.txt:
1.1 + 2.2 = 3.3
When files are done being used it is best practice to close them. This way the source file does not get corrupted, or become unreadable, because of not being closed properly. It may also be advantageous to close a file to:
Open a new file into the file variable.
Go back to the beginning of the input file.
Erase an output file.
To use an input file as an output file (by closing the file as an input file then opening it to an output file).
To close a file use:
fstream.close()
Where fstream is replaced with the identifier of any file variable ifstream or ofstream. For example:
#include <fstream> using namespace std; int main () { // file variables ifstream iFile; ofstream oFile; // open the files iFile.open(“input.txt”); oFile.open(“output.txt”); // read/write the files int i = 0; iFile >> i; oFile << i << endl; // close the files iFile.close(); oFile.close(); return 0; }
Once iFile.close() the input file iFile can no longer be read from because the link to the file input.txt is disassociated. The same goes for the output file oFile once oFile.close() runs. The file can no longer be written to because the link from oFile to output.txt is disassociated.
So in general, there are 5 steps to file I/O:
Include the header file.
Create variable(s) for input or output files.
Associate the file variables with the input or output source files.
Use the file variables with >>, getline() or <<.
Close the file(s).
Write a program that reads a player’s health and a monster’s health from a file, damages the monster’s health by 3 sword damage from the player, damages the player’s health by 1 fireball damage from the monster, and then saves the new healths back to the original file.
Consider the following text file healths.txt:
player monster 20 10
The first row character monster informs what is on the second row (called the headers), correlating the 20 to player and 10 to monster. Since this is the healths.txt that would lead one to infer the 20 refers to the player’s health and the 10 refers to the monster’s health. These values can be read from the file after the file has been associated with an input file variable:
#include <fstream> using namespace std; int main() { // variables string headers = ""; int characterHealth = 0, monsterHealth = 0; ifstream input; // open health file input.open(“healths.txt”); // get headers together getline(input, headers); // get both healths individually input >> characterHealth >> monsterHealth; // done inputting so close input.close(); return 0; }
First the input file variable input has the file healths.txt associated with it using input.open(“healths.txt”). Then the headers are read in from the file using getline(input, headers). The reason getline() is chosen over stream extraction (>>) is because stream extraction stops at whitespace. Since it is not necessary to do anything with the individual headers at this point it is better to just keep them together by getting the whole line of headers into the headers variable using getline() which stops reading at the newline character at the end of a line.
Once the headers have been read in the two healths can be read into two integer variables using input >> characterHealth >> monsterHealth. This time stream extraction is used to keep the two values separate (and since they are integers getline() can not be used on them). This way the two healths can be damaged using arithmetic in the next step. Once everything has been read from the file the link from input to healths.txt is disassociated so it can no longer be read from.
The healths can then be damaged (3 sword damage to the monster and 1 fireball damage to the player) using arithmetic:
#include <fstream> using namespace std; int main() { // variables string headers = ""; int characterHealth = 0, monsterHealth = 0; ifstream input; // open health file input.open(“health.txt”); // get headers together getline(input, headers); // get both healths individually input >> characterHealth >> monsterHealth; // done inputting so close input.close(); // character attacks monster with sword monsterHealth -= 3; // monster attacks character with fireball characterHealth -= 1; return 0; }
The compound assignment operator -= is used to simply subtract the appropriate damage from the appropriate variables then override the variable with the result of the subtraction. The new healths (after taking damage) can then be saved to the file (which originally was the input file):
#include <fstream> using namespace std; int main() { // variables string headers = ""; int characterHealth = 0, monsterHealth = 0; ifstream input; ofstream output; // open health file input.open(“health.txt”); // get headers together getline(input, headers); // get both healths individually input >> characterHealth >> monsterHealth; // done inputting so close input.close(); // character attacks monster with sword monsterHealth -= 3; // monster attacks character with fireball characterHealth -= 1; // open the file that was an input file as an output file output.open(“health.txt”); // output (save) the new health to the file output << headers << endl << characterHealth << " " << monsterHealth << endl; // close the file now that the healths are updated output.close(); return 0; }
The file health.txt can be associated to an output file variable only because it has already been disassociated from an input file variable using input.close(). When health.txt is opened as an output file it is deleted and recreated so the file is blank and the updated healths can be written to it.
First the headers, which have the value player monster are output to the file to keep the headers consistent in the file at all times. Then the two updated healths, 19 for the player and 7 for the monster, are written to the file on a separate line. The file health.txt is then disassociated from the output file variable using output.close().
This produces the following in healths.txt:
player monster 19 7
Files can also be opened via user input:
#include <iostream> #include <fstream> using namespace std; int main () { // variable for file name string filename = “”; // input file variable ifstream iFile; // open the input file cout << “Input file name: “; cin >> filename; iFile.open(filename); // file opened cout << “file opened\n”; // close file iFile.close(); return 0; }
But in this scenario the user is not guaranteed to enter a valid file name or path to a valid file. When this happens the program will not crash, but will continue to outputting “file opened\n” then closing the file that did not actually open. To check if a file has been opened into either an input or output file variable check:
fstream.is_open()
Which when fstream is replaced with a properly declared input or output file variable will return true if a file has been opened in the variable, and false if a file has not been opened in the variable. So when output after opening the file:
... iFile.open(filename); cout << iFile.is_open() << endl; ...
If the entered path to the file name is valid then 1 will be output (representing true), and if an invalid file name or path to a file is entered then a 0 will be output (representing false). Rather than output this it can can be used in a one-way selection to determine if the file was opened or not:
#include <iostream> #include <fstream> using namespace std; int main () { // variable for file name string filename = “”; // input file variable ifstream iFile; // open the input file cout << “Input file name: “; cin >> filename; iFile.open(filename); // check if file opened if (!iFile.is_open()) { // error and terminate if file didn’t open cout << “Error: Invalid file name\n”; return 0; } // file opened cout << “file opened\n”; // close file iFile.close(); return 0; }
Here the not operator ! is used to flip the value of iFile.is_open() so that when the file does not open and false is received it is flipped to true causing the selection to run. When the selection runs an error message is displayed and the program terminated. But terminating the program is not always practical. Often the failure needs to be recovered. Thus rather than terminating the program, the program can just loop back up to where the input file name is received to get a new file name from the user to try to open again:
#include <iostream> #include <fstream> using namespace std; int main () { // variable for file name string filename = “”; // input file variable ifstream iFile; // loop until valid file entered while (!iFile.is_open()) { // open the input file cout << “Input file name: “; cin >> filename; iFile.open(filename); // check if file opened if (!iFile.is_open()) { // error if file didn’t open cout << “Error: Invalid file name\n”; } } // file opened cout << “file opened\n”; // close file iFile.close(); return 0; }
Now the user will just continue to be told Error: Invalid file name and be prompted to enter a new file name until the user enters a valid file name and path to the file.
There may be multiple players fighting multiple monsters in the game. Add to the Saving Health After Damage example the ability to enter a file name to read the health from.
First the name of the file needs to be read in then it can be opened:
#include <fstream> #include <iostream> using namespace std; int main() { // variables string filename = "", headers = ""; int characterHealth = 0, monsterHealth = 0; ifstream input; ofstream output; // get file name cout << "Health File: "; // open health file getline(cin, filename); // open file input.open(filename); // get headers together getline(input, headers); // get both healths individually input >> characterHealth >> monsterHealth; // done inputting so close input.close(); // character attacks monster with sword monsterHealth -= 3; // monster attacks character with fireball characterHealth -= 1; // open the file that was an input file as an output file output.open(“health.txt”); // output (save) the new health to the file output << headers << endl << characterHealth << " " << monsterHealth << endl; // close the file now that the healths are updated output.close(); return 0; }
The user is prompted with Health File: and then is allowed to input the name of the file to the string variable filename. The filename entered can then be opened to the input file variable input using input.open(filename).
The filename entered by the user could incorrect though such as if error were entered, thus the opening of the file must be validated:
#include <fstream> #include <iostream> using namespace std; int main() { // variables string filename = "", headers = ""; int characterHealth = 0, monsterHealth = 0; ifstream input; ofstream output; // get file name cout << "Health File: "; // open health file getline(cin, filename); // open file input.open(filename); if (!input.is_open()) { cout << "Error: Invalid filename\n"; } // get headers together getline(input, headers); // get both healths individually input >> characterHealth >> monsterHealth; // done inputting so close input.close(); // character attacks monster with sword monsterHealth -= 3; // monster attacks character with fireball characterHealth -= 1; // open the file that was an input file as an output file output.open(“health.txt”); // output (save) the new health to the file output << headers << endl << characterHealth << " " << monsterHealth << endl; // close the file now that the healths are updated output.close(); return 0; }
If the file didn’t open then input.is_open() will be false which can be flipped to a true using the ! in !input.is_open(), which causes the selection to run if the file did not open which outputs Error: Invalid filename. But as written the program would just continue on, so the lines getting the filename can be wrapped in a loop so the program loops getting a filename until a valid one is entered:
#include <fstream> #include <iostream> using namespace std; int main() { // variables string filename = "", headers = ""; int characterHealth = 0, monsterHealth = 0; ifstream input; ofstream output; // open valid health file do { // get file name cout << "Health File: "; // open health file getline(cin, filename); // open file input.open(filename); if (!input.is_open()) { cout << "Error: Invalid filename\n"; } } while (!input.is_open()); // get headers together getline(input, headers); // get both healths individually input >> characterHealth >> monsterHealth; // done inputting so close input.close(); // character attacks monster with sword monsterHealth -= 3; // monster attacks character with fireball characterHealth -= 1; // open the file that was an input file as an output file output.open(“health.txt”); // output (save) the new health to the file output << headers << endl << characterHealth << " " << monsterHealth << endl; // close the file now that the healths are updated output.close(); return 0; }
Now as long as the program sees that !input.is_open() is true it will loop continuing to get a filename until the user enters a valid filename.
Add to the Allow Any Health File example the ability to handle files that have the healths in the order
monster player.
Consider the following text file healths2.txt:
monster player 10 20
As written the program will produce the following healths2.txt file when it is provided:
monster player 9 17
Since the program reads in the healths input >> characterHealth >> monsterHealth, which places the first value 10 into the variable characterHealth and the second value read 20 into the variable monsterHealth. The arithmetic is then done on the variables which subtracts 3 from the characterHealth (which is actually the monster’s health in this scenario) and 1 from the monsterHealth (which is actually the character’s health in this scenario).
To fix this this needs to be detected. In order to detect this the headers need to be checked to determine if they are out of order and if they are out of order the healths can be swapped:
#include <fstream> #include <iostream> using namespace std; int main() { // variables string filename = "", headers = ""; int characterHealth = 0, monsterHealth = 0; ifstream input; ofstream output; // open valid health file do { // get file name cout << "Health File: "; // open health file getline(cin, filename); // open file input.open(filename); if (!input.is_open()) { cout << "Error: Invalid filename\n"; } } while (!input.is_open()); // get headers together getline(input, headers); // get both healths individually input >> characterHealth >> monsterHealth; // done inputting so close input.close(); // swap monster's health if monster is first in file if (headers == "monster player") { // swap health int savedHealth = characterHealth; characterHealth = monsterHealth; monsterHealth = savedHealth; } // character attacks monster with sword monsterHealth -= 3; // monster attacks character with fireball characterHealth -= 1; // open the file that was an input file as an output file output.open(“health.txt”); // output (save) the new health to the file output << headers << endl << characterHealth << " " << monsterHealth << endl; // close the file now that the healths are updated output.close(); return 0; }
When headers is "monster player" then the healths are out of order so they can be swapped. To swap you want to essentially place a into b and b into a:
a = b; b = a;
But here the value of b gets placed into a (overriding the original value of a), so when b = a is run a is just placing a copy of the value of b back into b effectively duplicating b. To fix this a needs to be saved before it is overwritten:
s = a; a = b; b = s;
Now the value of a is saved into s before it is overwritten with the value of b, so when b needs to be overwritten it can be with s (which has the original value of a inside of it).
Back to the example this same logic follows when the healths are out of order when headers == "monster player" is true. The characterHealth is saved in savedHealth using int savedHealth = characterHealth. The characterHealth is then overwritten with monsterHealth using characterHealth = monsterHealth. Lastly the monsterHealth is overwritten with savedHealth using monsterHealth = savedHealth. The program then continues as explained previously.
But this is now without problems, in the output statement the output is just outputting the original headers (which in this scenario are monster player) then the character’s health is output followed by the monster’s. So this produces the output:
monster player 19 7
Which now gives the monster the player’s health and the player the monster’s health. So the headers also need to be swapped. They must first be read individually. Then if the first header is monster a swap must occur, so perform the swap on not only the healths but the headers as well. The individual headers can then be output to the file after swapping:
#include <fstream> #include <iostream> using namespace std; int main() { // variables string filename = "", firstHeader = "", secondHeader = ""; int characterHealth = 0, monsterHealth = 0; ifstream input; ofstream output; // open valid health file do { // get file name cout << "Health File: "; // open health file getline(cin, filename); // open file input.open(filename); if (!input.is_open()) { cout << "Error: Invalid filename\n"; } } while (!input.is_open()); // get headers individually input >> firstHeader >> secondHeader; // get both healths individually input >> characterHealth >> monsterHealth; // done inputting so close input.close(); // swap monster's health if monster is first in file if (firstHeader == "monster") { // swap health int savedHealth = characterHealth; characterHealth = monsterHealth; monsterHealth = savedHealth; // swap headers string savedHeader = firstHeader; firstHeader = secondHeader; secondHeader = savedHeader; } // character attacks monster with sword monsterHealth -= 3; // monster attacks character with fireball characterHealth -= 1; // open the file that was an input file as an output file output.open(“health.txt”); // output (save) the new health to the file output << firstHeader << " " << secondHeader << endl << characterHealth << " " << monsterHealth << endl; // close the file now that the healths are updated output.close(); return 0; }
Variables are added to hold the headers individually, the headers are then read in using input >> firstHeader >> secondHeader rather than getline(input, headers). This can be done because each time there is whitespace in the first line it is a separate header.
Since the headers are in separate variables the check headers == "monster player" needs to be changed. Now when the healths are out of order the monster is the first health, so firstHeader can be checked for the value monster and if it is then the healths and the headers can be swapped. The headers are swapped in a similar fashion as the healths, and in the output statement output << firstHeader << " " << secondHeader the headers are output one after another in their proper order. This forces the new file to be in the proper format player monster.
This produces the following in healths2.txt:
player monster
19 7
File - A named portion of secondary memory used to permanently store data.
Output File ofstream - Files that have data written to them by a program.
Input File ifstream Files that contain data to be read by a program.
To Be Added Later