Input and Output in C++

C++ user input (cin):

You might want to get input from a user. Here is an example of getting two numbers from the user and then trying to divide one by the other, building off the previous example of exception handling for dividing by zero.

cout is for output. cin is for input. cin.fail() will be true if the input failed, such as if you are expecting a numeric input from the user and they enter some text instead. If you use C instead of C++, you will use something called scanf() instead.

#include <iostream>

using namespace std;

int main() {

double x;

double y;

cout << “Let’s divide Y by X!” << endl;

cout << “Enter the X value: “;

cin >> x;

cout << “Enter the Y value: “;

cin >> y;

try {

if (cin.fail()) {

throw “type error”;

} else if (x == 0) {

throw “can’t divide by zero”;

} else {

cout << “Y divided by X is: “;

cout << (y / x) << endl;

}

} catch(const char* ex) {

cerr << “Failed because: “;

cerr << ex << endl;

}

return 0;

}

Check if file exists in C++:

To deal with files in C++, you’ll need to do #include <fstream>.

To make an input file stream object, you need to declare it with the filename of the file. This can be a relative path. For example, if a file is in the same directory, you can just do “something.txt” instead of the entire path, such as C:\whatever\something.txt or /home/steve/something.txt.

Unlike other languages, there doesn’t seem to be a function in C++’s ifstream that can simply let you know if a file exists or not. However, there is a function within the ifstream class that will let you check if a file is “good” or not, which means if you have permission to use it, if it’s not locked by another program, and if it exists. But if it’s not “good” then it could mean that any one of those things is the problem, so it’s not super specific.

#include <iostream>

#include <fstream>

using namespace std;

int main() {

ifstream myFile;

myFile.open(“test.txt”);

if (myFile.good()) {

cout << “you can use test.txt” << endl;

} else {

cout << “myFile is no good” << endl;

}

myFile.close();

return 0;

}

Try saving the above code as main.cpp, then compile it. Try running it with and without a file called test.txt in the same folder. You’ll see that, if there’s a file called test.txt in the same folder as your compiled main program, it will say “you can use test.txt.” But if test.txt doesn’t exist (or there’s something else that makes it not good), it will say “myFile is no good.” Also, notice how, after opening the file, you also need to close it with the close() function.

Paths

If you made a file but C++ doesn’t seem to detect it with the above example, make sure you’re putting the test.txt file in the correct directory, or that your path is right. For example, in CLion, Your main.cpp file will be in a project directory, like so:

C:\Users\alan\CLionProjects\chapter6_demos

But then the executable gets compiled and put into a subdirectory:

C:\Users\alan\CLionProjects\chapter6_demos\cmake-build-debug

So in that case, putting the test.txt file in the project folder (in my case, chapter6_demos) would be wrong, and your program wouldn’t be able to find it that way. You’d instead have to put the test.txt file in the cmake-build-debug folder. Or you can just use absolute or relative paths rather than only providing the file name and assuming the file will be in the same folder as the executable.

In Eclipse CDT (The C++ version of Eclipse), your main.cpp source code file might be stored in a directory similar to this (src meaning source code):

C:\Users\alan\eclipse-workspace\myproject\src

But the compiled .exe will be stored here:

C:\Users\alan\eclipse-workspace\myproject\Debug

Opening and closing files:

In the above code example, you might notice two lines that go along with the ifstream object: myFile.open(“test.txt”) and myFile.close(). You need to open a file with a file path using open(), and when you’re done with it, you need to use close().

Reading all the lines from a file in C++:

Make a file called test.txt and put this data in it:

here is line one

here is another line

this is line 3

yet another line

more lines

some more lines of text

this is line 7

line 8

second to last line

line 10 in this file

The following program will open a file, and if it’s good, it will get and cout every single line from the file until it reaches the end:

#include <iostream>

#include <fstream>

using namespace std;

int main() {

ifstream myFile;

myFile.open(“test.txt”);

if (myFile.good()) {

string lineFromFile;

cout << “you can use test.txt” << endl;

while (getline(myFile, lineFromFile)) {

cout << lineFromFile << endl;

}

} else {

cout << “myFile is no good” << endl;

}

myFile.close();

return 0;

}

getline() takes two arguments: one fstream, and one string. It takes a line from a file and then stores it in a string. For every time through the while() loop, the string lineFromFile will be assigned another string. For example, first it’s assigned the first line of the text file, then the second, then the third, and so on. while(getline()) means it will keep on doing this until it’s gone through the entire file, before it encounters the end of file (EOF).

Reading specific lines from a file in C++ and storing them in an array:

Maybe you have a configuration file, and each line in the file is a different setting for your program. Maybe every odd-numbered line will explain what the next line will do, and every even-numbered line has a value for it. Save the following text as config.txt:

Width:

1280

Height:

720

Debug mode:

false

Theme:

blue

Tool tips:

off

Night mode:

on

Auto start:

off

Language:

English

In the above example, lines 2-16 even are config settings, and lines 1-15 are names for the config settings. Please note that this isn’t the only way to structure a config file, but I’m just trying to keep it simple by making one thing on each line.

Here’s how you would load the settings when the program starts:

#include <iostream>

#include <fstream>

using namespace std;

int main() {

ifstream myFile;

myFile.open(“config.txt”);

if (myFile.good()) {

string lineFromFile;

string settings[8];

int lineNumber = 1;

while (getline(myFile, lineFromFile)) {

//only store even numbers

if (lineNumber >= 2 && lineNumber % 2 == 0) {

settings[(lineNumber/2) – 1] = lineFromFile;

}

lineNumber++;

}

cout << “Loaded program settings from file” << endl;

cout << “Window width: ” << settings[0] << endl;

cout << “Window height: ” << settings[1] << endl;

cout << “Debug mode: ” << settings[2] << endl;

cout << “Window theme: ” << settings[3] << endl;

cout << “Window tool tips: ” << settings[4] << endl;

cout << “Night mode: ” << settings[5] << endl;

cout << “Program auto-start: ” << settings[6] << endl;

cout << “Window language: ” << settings[7] << endl;

} else {

cout << “myFile is no good” << endl;

}

myFile.close();

return 0;

}

In the above example, the while(getline(file, string)){} loop goes through every line in the file, but it has an if block inside it that checks if the line number is desirable. An int variable is used and incremented to keep track of which line you’re on. There are many other ways to do this too.

Reading values from a CSV (comma-separated values) file in C++ and using them for changing instance variables for elements of an object array:

Let’s say you want to keep track of computer science courses at a given university. You could store them in a CSV file. Realistically, you’d use a database, but databases are covered in a later chapter. For now, courses will be stored in a CSV file, and parsed using a C++ program, which includes a Course class, which can be used to make course objects. The demonstrative program for this sub-section will include reading CSV values from a CSV file, creating objects with values from said CSV file, and then displaying them for the user to see.

Save the following data as courses.csv:

100, Computers and Society, 55, 3

109, The Information Age, 60, 7

200, Programming I, 40, 1

201, Discrete Structures, 30, 0

207, Programming II, 35, 2

300, Client Side Web Development, 40, 10

301, Computer Organization, 30, 6

302, Systems Programming, 45, 4

304, Data Structures, 20, 5

307, Programming Languages, 25, 2

308, Operating Systems, 30, 8

310, Topics in Computer Science, 40, 12

311, Computer Architecture, 25, 0

314, Independent Study, 5, 1

316, Modern Data Base Management, 40, 15

317, Event-Driven Programming, 25, 3

319, Fundamentals of Software Engineering, 35, 8

320, Object-Oriented Programming, 40, 11

321, Server Side Web Development, 30, 7

322, Statistical Computer Program Packages, 30, 18

323, Cyberlaw, 20, 0

324, Introduction to design of Algorithms, 40, 9

325, Theory of Computation, 25, 0

326, Computer Use for Numerical Methods, 15, 1

327, Computational Methods in Biology, 15, 4

The above are a few examples of classes for a computer science department. The first number is the course number, such as CS310, the next value is the name of the course, the value after that is the capacity of the class, and the final int on each line is the number of remaining spots in the class.

Now we’ll use a program that will define a class for Course objects, open the CSV file, make objects with values from the file, and then let the user view them:

#include <iostream>

#include <fstream>

using namespace std;

class Course {

private:

int courseNumber;

string name;

int capacity;

int remaining;

public:

Course() {

courseNumber = 0;

name = “Default”;

capacity = 0;

remaining = 0;

}

Course(int courseNumber, string name, int capacity, int remaining) {

this->courseNumber = courseNumber;

this->name = name;

this->capacity = capacity;

this->remaining = remaining;

}

int getCourseNumber(){

return courseNumber;

}

string getName(){

return name;

}

int getCapacity(){

return capacity;

}

int getRemaining(){

return remaining;

}

void setCourseNumber(int courseNumber){

this->courseNumber = courseNumber;

}

void setName(string name){

this->name = name;

}

void setCapacity(int capacity){

this->capacity = capacity;

}

void setRemaining(int remaining){

this->remaining = remaining;

}

};

int main() {

ifstream csvFile;

csvFile.open(“courses.csv”);

Course* courseArray = new Course[25]();

if (csvFile.good()) {

//where the CSV stuff happens

int counter = 0;

//assumes there are 25 entries in csvFile

//because that’s the size of the array

while (csvFile.good()) {

string courseStr = “-1”;

string nameStr = “A”;

string capacityStr = “-1”;

string remainingStr = “-1”;

getline(csvFile, courseStr, ‘,’);

getline(csvFile, nameStr, ‘,’);

getline(csvFile, capacityStr, ‘,’);

getline(csvFile, remainingStr);

courseArray[counter].setCourseNumber(stoi(courseStr));

courseArray[counter].setName(nameStr);

courseArray[counter].setCapacity(stoi(capacityStr));

courseArray[counter].setRemaining(stoi(remainingStr));

counter++;

}

//done populating array

//now printing the table

cout << “CRN ” << “Name”;

for (int i = 0; i < 33; i++) {

cout << ” “;

}

cout << “Cap Rem” << endl;

for (int i = 0; i < 25; i++) {

cout << “CS” << courseArray[i].getCourseNumber() << “:”;

cout << courseArray[i].getName();

int nameLength = courseArray[i].getName().length();

if (nameLength <= 37) {

for (int j = 0; j <= (37 – nameLength); j++) {

cout << ” “;

}

}

if (courseArray[i].getCapacity() < 10) {

cout << ” “;

}

cout << ” ” << courseArray[i].getCapacity();

if (courseArray[i].getRemaining() < 10) {

cout << ” “;

}

cout << ” ” << courseArray[i].getRemaining();

cout << endl;

}

} else {

cerr << “problem with file” << endl;

}

csvFile.close();

delete courseArray;

return 0;

}

Here is the output of the above program:

As you can see, the program read CSV values from a file, used them in conjunction with setter methods to change objects, and then displayed the results in a nice table-like way. Instead of just using cout to display the object info using the getters from the Course class, it could have also been used to do things like search or sort the list of classes, but this code example is long enough as it is, so I made it just display things in a nice-looking way.

Don’t be too concerned about the specific details of how the code formats the text for the output. That’s not the point here. What is the point of the above code example is to show that you can change objects using files rather than hard-coding data into your C++ source code files. When you have a lot of data, put it in data-related files, like .txt, .csv, .xml, .json, and so on. It would be silly to put all that CS course data into a main.cpp, rather than separating it. So this is one advantage of being able to read from a file.

One distinction between .csv and .txt is that .csv values are delimited by commas, except for the last one on a line, which is delimited from the next value by a newline.

Course* courseArray = new Course[25](); – the * and new mean it’s using pointers and manual memory management, which is covered more in the next section. Don’t worry too much about that for now. But just know that this line created a Course array with a size of 25. It also uses the no-argument constructor, which creates objects but only gives them default values, as specified in the class definition before the main function in the above code example.

Basically, a Course class and a Course array were made so that the course info from the CSV file could be put into objects rather than strings. Primitive data types are very limited in what they can do, and there is more power in being able to alter objects of user-defined classes.

int counter = 0; – a counter is created and initialized to zero. It will be used for the array indices. When the counter is zero, the zeroth element in the Course array is being changed. When it’s one, then it’s using index [1] to change the next one. It keeps going for all 25 courses in the courses.csv file.

while (csvFile.good()) – while loop for getting data out of the CSV file so that it can be used to change the Course objects in the Course array. As long as there are no problems in the csvFile ifstream object, such as the file not existing or reaching the end of the file, it will continue looping. But I know the .csv has a fixed length of 25 in this example. But you might be dealing with reading from files of an indeterminate length, so that’s something to consider.

Within this while loop, it gets the comma-separated values from the file:

getline(csvFile, courseStr, ‘,’);

getline(csvFile, nameStr, ‘,’);

getline(csvFile, capacityStr, ‘,’);

getline(csvFile, remainingStr);

getline() in this case actually isn’t getting full lines, but instead, it’s getting values that are separated, or delimited, by commas.

Recall that each line in the .csv file looks something like this:

100, Computers and Society, 55, 3

A value, a comma, another value, a comma, a third value, a third comma, and a fourth value. The next value (if any in the file) will be after a newline.

Because there are four pieces of data on one line, with three separators, you need to use a third argument with the getline() function. The first argument is the ifstream object, which is a file you are reading from. The second argument is a string to store the stuff that you’re getting from the input file stream. The third optional argument is how you can specify the delimiter. In this case, I am separating values by commas and spaces, so I use the ‘,’ char to denote what the delimiter is.

Even though the Course class uses three ints and a string, the getline() function can only be used with strings, so I made temporary string representations of each, such as courseStr, nameStr, capacityStr, and remainingStr. The only one that is really supposed to be a string is nameStr, but the others are just strings in lieu of ints because numeric types can’t be used with this function. But they will need to be changed later.

courseArray[counter].setCourseNumber(stoi(courseStr)); – at this point in the program, courseArray[] elements are default-initialized because of the no-arg constructor, so they exist, but don’t have their unique and proper data yet. The class has your typical getter and setter functions. stoi() is a function used for converting strings to integers, which is necessary for the numeric instance variables of the Course class.

The process is to open a file, use getline() with an appropriate delimiter to get part of the line that you want, and then change types as necessary and then use setter a.k.a. mutator functions with default-instantiated objects in order to basically turn your .csv file into objects in RAM while your program is running. This is a simple yet powerful concept.

counter++; – for each time through the while loop, the counter variable is incremented so that the array index will be valid for the next one. First, courseArray[0] is being changed, then courseArray[1], and so on.

The code after that is just printing table-formatted data from the array of Course objects, showing that they have indeed been changed to reflect the data from the input file.

There are countless possibilities for how this kind of stuff can be useful. While I only used a class info example, you can do so much with reading data from a file and putting it into objects that you can manipulate.

But so far, we’ve only covered file input in C++. The other important half of IO is output.

Create a new file, but only if there isn’t already an existing file with the same filename:

Now that you know a lot about user input and file input, it’s time to learn about file output. You already learned about it in Java and Python. The implementation is slightly different in C++, but the fundamental concepts are the same.

Reading from a file requires ifstream, which is the input file stream class. But for creating a file, you need to use ofstream, which is the output file stream class.

#include <iostream>

#include <fstream>

using namespace std;

int main() {

string nameOfNewFile = “filename444454.txt”;

ifstream checkIfExists;

checkIfExists.open(nameOfNewFile);

bool alreadyExists = checkIfExists.good();

checkIfExists.close();

if (!alreadyExists){

cout << “making new file” << endl;

ofstream newFile;

newFile.open(nameOfNewFile);

newFile << “”;

newFile.close();

} else {

cout << “error: can’t make new file” << endl;

cout << “file with filename of ” << nameOfNewFile;

cout << ” already exists” << endl;

}

return 0;

}

In the above example, I actually used ifstream to check if a file exists or not. If you want to make a new file, the program first checks if there’s an existing file by the same name. You wouldn’t want to accidentally delete an existing file and make a new blank file with the same name. That would lead to data loss, which is bad. So using ifstream, and using the good() function of the ifstream class, we can assign the result of fstream.good() to a boolean variable called alreadyExists. alreadyExists will be true if a file with that filename already exists and false if it does not.

The subsequent if/else will check to see if the file with that filename does not exist. ! means does not exist. If the file doesn’t exist already, it will proceed with making a new file. It writes an empty string to it, meaning the file is blank and has a total file size of 0 bytes (file name is handled by the file system and thus doesn’t count towards the file size of a file). If a file already exists with that name, the program lets the user know and then doesn’t make a new file.

And just like with input streams, you need to open and close output streams too.

Deleting an existing file in C++:

Be careful when writing code that deletes anything. I personally like to add text prompts asking if the user is sure that they want to delete it. Maybe they accidentally typed the wrong thing. By adding confirmation dialogue, you reduce the risk of someone accidentally deleting something that really shouldn’t be deleted.

You not only need to check if the user really wants to delete the file, but you also need to check if the file exists, which was covered in the above sub-section.

Here is how to delete a file:

#include <iostream>

#include <fstream>

using namespace std;

int main() {

string filename = “filename.txt”;

ifstream checkIfExists;

checkIfExists.open(filename);

bool fileExists = checkIfExists.good();

checkIfExists.close();

if (fileExists){

cout << “are you sure you want to delete “;

cout << filename << “? y / n: “;

string choice;

cin >> choice;

if (choice == “y” || choice == “yes”) {

const char* constFilename = filename.c_str();

int success;

success = remove(constFilename);

if (success == 0){

cout << “file was successfully deleted” << endl;

} else {

cout << “error with deleting file” << endl;

}

} else {

cout << “choice was no; file will not be deleted” << endl;

}

} else {

cout << “file doesn’t exist” << endl;

}

return 0;

}

It does what I mentioned earlier: checks if the file exists, asks the user if they want to delete it or not, and then it can finally delete it if the file exists and the user confirms with “y” or “yes” that they are sure they want to remove it. Deleting files in C++ is achieved using the remove() function. However, it does not accept strings. That is the only real oddity of the above code.

The remove() function takes a const char* arg. If that confuses you, it’s referred to as a “C string,” hence the function name c_str() to turn a string into a C string. If you’re wondering what a C string is, or why you’d use it, it’s a relic from the past, back when languages didn’t have good support for strings. In this day and age, regular strings are better. But people used C strings in C because that’s what they had at the time.

So if you want to use remove() with a string, just do remove(stringVar.c_str()) and it should be fine.

The other thing to note about the remove() function is that it returns a numeric return value. If it returns 0, it means it went fine, just like a main function’s int main() returning 0 if it was good. A non-zero return value means something went awry.

Writing over a file in C++:

The following code replaces an existing file with a blank file:

#include <iostream>

#include <fstream>

using namespace std;

int main() {

string filename = “filename.txt”;

ofstream outFile;

outFile.open(filename);

outFile << “”;

outFile.close();

return 0;

}

Any previous contents of the file will be lost. In some cases, you actually want that. In other cases, you might prefer to append to the end of a file rather than writing over a file.

Appending to a file in C++:

Appending is adding stuff to the end of an existing file without deleting the stuff that was already in it. Overwriting is like reassignment, whereas appending is like concatenation.

Make a file called append_test.txt, then put the following code in your main.cpp, compile it, and then run it multiple times, checking the contents of append_test.txt in between each run:

#include <iostream>

#include <fstream>

using namespace std;

int main() {

string filename = “append_test.txt”;

ifstream appendFileExists;

appendFileExists.open(filename);

bool fileExists = appendFileExists.good();

appendFileExists.close();

ofstream appendFile;

appendFile.open(filename, ios_base::app);

if (fileExists){

cout << “file exists, appending…” << endl;

appendFile << “this has been appended!” << endl;

} else {

cout << “file doesn’t exist” << endl;

}

appendFile.close();

return 0;

}

It checks if the file exists, and if it does, it will open an output stream, the same as any other file writing stuff, but with one key difference: it is opened with appendFile.open(filename, ios_base::app). In the std namespace, there is something called ios_base. Within ios_base, there is something called app. It means append. Because the above code is using namespace std; you don’t need to specify std:: before ios_base::app even though it is technically std::ios_base::app. There are many other things in std that you don’t need to specify each and every time you use them when you use std as the namespace – for example, cout and endl.

But what is ios_base? Within the <iostream> library, there are many headers, one of which is <ios>. Within the <ios> header, which is the header for input and output base classes, there is the ios_base class, which is a base class for input and output streams. ios_base is a parent (base) class for many more specific types of IO stream classes. As such, it has a lot of things that are shared by all of them, because they inherit from ios_base. Within ios_base, there are many things. One category of stuff within the ios_base class is its member types. One such member type is called openmode. There are many different modes in openmode, one of which is append.

Final IO thoughts

IO can be powerful if you combine many different types of it. You can output text to the terminal so that the user can see what’s going on. You can accept user input to change what the program does, like opening a file with a specific filename that the user provides, or choosing an option from a numbered list, where the user can type a number to do something in the program. You can read from one file to see data, change objects, and even use it for writing data to another file. If you make a program where a user can edit data, your program would ask the user what file they want to open, then it would read from the file, then put it into some sort of object or structure in memory, then the user can manipulate it more using more standard input, and the changes can be reflected with using cout to output the changes to the screen. Then, when they are done, the program can take the objects, and use something like a toString function (or just getters) to then prepare strings to write to a file. You can also search for terms within a file rather than parsing it from beginning to end.

There is also binary file IO, which means dealing with files that have non-textual data, such as photos, videos, or songs. But that is beyong the scope of this book.

← Previous | Next →

C++ Topic List

Main Topic List

Leave a Reply

Your email address will not be published. Required fields are marked *