Simple Prompt Input and Output
Manipulators and flags for Formatting
Inputting and Outputting Object Types
Dissecting Input Example (Date Validation)
Simple Prompt Input and Output
Most commonly used programs use a combination of both input and output to interact and present users with meaningful information (output) from their data (input). Any program without input or output facilitates no use!
Command line input and output is a textual interaction between a program and user. Although a Command Line interface is quite dull, to say the least, it is the most basic and quickest to implement.
There are several ways that a C++ program can perform input and output, the most common is via the use of streams. The intuition for the word stream is that a program “sees” only the input or output and not the origin (source) or destination of the data. A common analogy is the flow of water past your feet - you see the water at your feet but might not know where the water has come from nor where it is going.
An output example.
#include <iostream>
namespace std { };
using namespace std;
int main() {
cout << "Hello, world.\n"; // The simple "Hello World" example.
return 0;
}
Using cout with insertion operators (”<<") should always display to the terminal screen.
Inputting is just as easy as outputting when using cin in conjunction with the extraction operators (">>”). cin is best thought as syntax for getting input from a keyboard. However, when using the cin approach with the extraction operators all white space is ignored and skipped. Using multiple extraction operators for user input may yield unexpected run-time effects when dealing with characters - commonly referred to as buffer problems - due to the unpredicability of users and their quirky input.
The extraction operators are best reserved for numeric input or for predictable formatted input - usually from a prepared file.
cout is derived from the ostream class and cin is derived from the istream class. Inheritance is of great use and is explained latter.
The safest way to acquire user input is by use of the overloaded member function get() which is common practice amongst programmers.
/* File: lab2002.cpp; Mode: C++; Tab-width: 3; Author: Simon Flannery; */
#include
namespace std { };
using namespace std;
int main() {
char unit; bool boolExit = true;
do {
cout << "Enter Compass Direction: ";
cin.get(unit); // Get a single character.
while (cin.get() != '\n'); // Clear Buffer.
switch (unit) {
case 'n':
case 'N': cout << "North."; break;
case 's':
case 'S': cout << "South."; break;
case 'e':
case 'E': cout << "East."; break;
case 'w':
case 'W': cout << "West."; break;
default: cout << "Check Your Compass.\n"; boolExit = false; break;
}
} while (!boolExit);
return 0;
}
In the preceding example the program requests user input, where the program expects a single character. However, users would not be aware of this single character requirement and it is therefore the role of the programmer to foresee and handle all possible input - including “silly” user input.
In the example, only the users first character entry is stored and validated. All other trailing characters are “eaten” thanks to the line: while (cin.get() != ‘\n’);. This will help to avoid buffer issues and stop the do… while loop being iterated again (and again and again) getting each character one at a time from a long string entry and therefore preventing a dump of “Check Your Compass”.
It should be clear that without clearing the buffer undesirable run-time effects can be encountered. Be astute when working with streams as buffer errors are very common. Remember to always clear the buffer!
Programs should also be able to handle user input of different character lengths and store input in a memory efficient manner. This can be achieved via the use of pointers.
Remember that a pointer is just a variable which holds the address of a starting block (the first address) of memory which has its size reserved on the heap at run-time. A pointer can “point” to any type of variable. A pointer can even reference another pointer.
#include
#include
namespace std { };
using namespace std;
int main() {
char buffer[101], * said;
cout << "Enter: ";
cin.get(buffer, 101, '\n');
while (cin.get() != '\n');
said = new char[strlen(buffer) + 1];
strcpy(said, buffer);
cout << "Echo: " << said;
delete [] said;
return 0;
}
The preceding example first declares a normal cstring, buffer, with a generous size. Next, the size of a new dynamic cstring is assigned by determining the length of buffer via the strlen() function and one is added to allow for the null terminating character. Clearly this saves memory if there are multiple cstrings to be stored, as opposed to assigning the same fixed length to each cstring.
Since output may want to be “streamed” to different destinations, for example, to the humble screen or text file, inheritance is of use.
#include
#include
namespace std { };
using namespace std;
void stream2(ostream& os, char*);
int main() {
stream2(cout, "Hello World.\n");
char* say = "And Hello again.\n";
stream2(cout, say);
ofstream outFile;
outFile.open("sample.txt");
stream2(outFile, say);
outFile.close();
return 0;
}
void stream2(ostream& os, char* say) {
os << char2Say;
return;
}
In the preceding example the ostream class is used as a parameter for the function stream2(). This is because of inheritance which allows many output streams, including file streams of type ofstream and the standard cout which is of type ostream, to the passed into the function body.
Also some may be familiar with char say[] = “Whatever”; rather than char* say = “Whatever”;. Most people would say that is no difference between these code fragments. So, is there a difference between these? Yes. Using the array notation 9 bytes of storage in the static memory block are used, one for each character and one for the NULL character. However, in the pointer notation the same 9 bytes are required, plus a couple of bytes to store the pointer variable reference char2Say.
One powerful feature of C++ is inheritance, which is the use of derived classes. Put simply, a derived class uses an existing class but adds unique functions (for extra features) to form a new class. For example, both cout and ofstream were derived from the ostream class - the ofstream class adds extra member functions such as open() and close(). And both cin and ifstream were derived from the istream class.
Notice that a stream may be an argument to a function with the only restriction being that the function formal parameter must be a call-by-reference, not a call-by-value.
A summary of some useful stream control functions:
istream::get() will extract and return a single character from the istream - including a white space character.
istream::get(char&) will extract a single character from the istream and store the character to the reference argument alias. Again, this includes white space characters.
istream::get(char*, int, char) will extract consecutive characters from the istream until either the integer limit - 1 is reached, the delimiter character is found, or the end of the istream is reached. The extracted characters are stored in the given cstring followed by the null terminator. If the delimiter is found, it is not extracted nor stored. Again, this includes white space characters.
ostream::put(char) will put a single character into the ostream.
istream::putback(char) will “put back” a character into the istream. This character must be the character previously extracted, else the result is undefined.
Manipulators and flags for Formatting
Manipulators are function calls which are normaly placed after the insertion operator (”<<") just as if the manipulator were a item to output which the output stream identifies. Manipulators aid with formatting output by changing the state of a stream.
Flags also aid with the dressing of input and output. A flag is an instruction to do something in one of two possible ways. The history of the term "flag" is thought to have been derived from saying "do this, when the flag is up (or down)".
#include
#include
int main() {
using namespace std;
double num, mult;
cout << "Pleae Enter two Numbers: ";
cin >> num >> mult;
cout.setf(ios::fixed);
cout.setf(ios::showpoint);
cout.setf(ios::showpos);
cout << "The product of " << num << " and " << mult << " equals "
<< setfill('*') << setw(10) << setprecision(3)
<< num * mult << "."
// Output product in a width
// of ten '*'s (right aligned is default)
// with decimal point, three decimal places
// and positive or negative sign.
<< endl;
return 0;
}
setfill(), setw(), setprecision() and endl are common examples of output stream manipulators. When setfill() and setw() are called respectively, a width of characters, set via setw(), is filled with the character given in setfill(). Valid output must be placed after these calls for the effect to be visible.
Useful Predefined output Manipulators, from header file
endl - When inserted into an output stream, a newline character is placed in the stream and then the buffer is flushed.
ends - When inserted into an output stream, a null-terminator character is placed in the stream.
So what’s the difference between endl, ‘\n’ and “\n”? Timing. The main performance difference relates to time. ‘\n’ is a little faster than “\n” and is faster than endl. The reason why endl costs time is that it flushes the current output buffer (via expensive write calls) as well as streams a humble newline character.
Useful Predefined output Manipulators, from header file
setw(int) - Sets the field width for following output, where a mono-spaced character is the width of one unit.
setfill(char) - Sets a fill character to be used as padding in the width set by setw(). Usually called before setw().
setprecision(int) - Enforces or restricts the decimal part of the value to a set number of decimal places.
setbase(int) - Converts all integers (in any base) in post output stream calls to the assigned base. Seems to only work for integers.
Input streams may also have manipulators, but they are not as common. One of the only standard input manipulators is ws which skips all white space when using the extraction operators - however, this feature is automatic when using the extraction operators.
Calls to the member function setf() (and unsetf()), are inherited from the ios class, and used to set (and unset) flags for streams in future stream calls. ios::showpos is an example of an enumerator representing a particular flag value.
Useful Flags (setting and unsetting), from header file
ostream::setf(ios::fixed)
ostream::unsetf(ios::fixed) - When set, floating point numbers are not expressed in e-notation.
ostream::setf(ios::scientific)
ostream::unsetf(ios::scientific) - When set, floating point numbers are express in e-notation.
ostream::setf(ios::showpoint)
::ostream::unsetf(ios::showpoint) - When set, a decimal point and trailing zeros are always shown. Normally used with ios::fixed and precision().
ostream::setf(ios::showpos)
ostream::unsetf(ios::showpos) - When set, the plus symbol is output before a positive value.
ostream::setf(ios::right)
ostream::unsetf(ios::right) - This is set by default, and when set, aligns output called after the setw() manipulator to the right of the field.
ostream::setf(ios::left)
ostream::unsetf(ios::left) - When set, aligns output to the left of a field called after the setw() manipulator.
ostream::setf(ios::boolalpha)
ostream::unsetf(ios::boolalpha)
Custom manipulators can be defined, for example as follows.
#include
namespace std {
ostream& tail(ostream& os) {
return os << "...";
}
}
int main() {
std::cout << "Hello" << std::tail << " World." << std::endl;
return 0;
}
In the preceding example, a globally defined manipulator called “tail”, which takes an ostream reference argument, is defined to insert three periods into any output stream. Since streams are changed by reading or writing, the call-by-reference parameter syntax is used.
The return type of the function was also of type ostream because this allows “chains” of “<<" to be called.
For example, std::cout << "Hello" << std::tail << " World." << std::endl;
is really, (((std::cout << "Hello") << std::tail) << " World.") << std::endl; where the contents of each parentheses returns an object of type ostream (cout) for the following "<<" to use. Also "<<" is just a normal function call.
Inputting and Outputting Objects Types
Object Types (Classes), at first, don’t seem to integrate well with stream input and output since an object is a group of smaller variables called member variables and functions called member functions (or “methods” if your background is Java). A variable of a defined object cannot be simply placed in an input or output stream because the related function calls “<<" and ">>” have not been overloaded to accept these new and custom types.
It would, however, be more convenient and effective to simply input or output a defined object with all or just a select few of its member values! The ability of overloading makes this possible and very easy. In this case, the extraction (”>>”) and insertion (”<<") operators is overloaded.
#include
#include
#include
namespace std { };
using namespace std;
class person {
public:
person();
~person();
friend ostream& operator<<(ostream&, const person&);
friend istream& operator>>(istream&, person&);
private:
char* title, * name;
int age;
};
person::person(): title(NULL), name(NULL), age(0)
{
}
person::~person()
{
delete [] title;
delete [] name;
}
const int LF = '\n'; // New Line, or Line Feed.
istream& operator>>(istream& is, person& p) {
char buffer[101];
cout << "Please Enter Title: ";
is.get(buffer, 101, LF);
while (is.get() != LF);
p.title = new char[strlen(buffer) + 1];
strcpy(p.title, buffer);
cout << "Please Enter Name: ";
is.get(buffer, 101, LF);
while (is.get() != LF);
p.name = new char[strlen(buffer) + 1];
strcpy(p.name, buffer);
cout << "Please Enter the Age of "
<< p.title << " "
<< p.name << ": ";
is.get(buffer, 4, LF);
while (is.get() != LF);
p.age = atoi(buffer);
return is;
}
ostream& operator<<(ostream& os, const person& p) {
return os << p.title << " "
<< p.name << ", is of age "
<< p.age << "." << endl;
}
int main() {
person p;
cin >> p; // Now, this is easy!
cout << "Input Information: " << p << endl;
return 0;
}
In the preceding example, the standard input and output streams have been overloaded to accept arguments of type istream and ostream and the developers defined type of person. Now, a variable of type person can be used in the same way as the standard data types when they are placed in an input or output stream.
The advantages should be clear. Placing a type of object independent of its member variables in an input or output stream not only reduces code but makes it easier to interpret and understand.
Simple File Input and Output
Most peripheral input and output, such as keyboard and screen, deal with temporary data. Once the program exits, all data is lost. Files, however, provide a way to store data in a permanent state (in secondary storage). Files that a program utilises may exist both before and after a program is run and exited. Files may also be created or destroyed when a program is run.
A common misconception is that
Opening and retrieving data from a file is easy.
#include
#include
namespace std { };
using namespace std;
int main(int argc, char** argv) {
if (argc < 2) { // If no (file) argument...
return 0; // ...Clean Exit.
}
double get = 0, total = 0;
ifstream inFile;
inFile.open(argv[1]);
while (!inFile.fail() && inFile >> get) {
total += get;
}
inFile.close();
cout << "The total Summation of the file: " << total << "." << endl;
return 0;
}
The preceding example opens a file and expects numeric data from the file. Due to the nature of extraction operators, and by default, all white space is skipped.
The advantage of skipping all white space is that the numeric data within the file could be separated by either new lines, spaces or by both a messy combination of new lines and spaces!
The while statement terminates when all consecutive numeric data has been read. The extraction operators can be used to terminate a control loop because they technically return a boolean value - true or false. The extraction operators preform best for numeric input, and characters.
Copying files is a popular task, but there is a trick. The actual file buffer must be referenced, not the class buffer.
#include
namespace std {};
using namespace std;
int main() {
ifstream inFile("in.txt");
ofstream outFile("out.txt");
if (inFile.fail() || outFile.fail()) { return 0; }
// outFile << infile; // Would seem correct, but Fails!
outFile << inFile.rdbuf(); // The correct method.
inFile.close();
outFile.close();
return 0;
}
It is important to observe that the provided stream classes are text-oriented - To be Universial and Fair for the sake portability.
When saving (outputting) to an external file, the external file is automatically created or replaced by default.
If, however, a file is not to be created or replaced, it must be known if the file already exists. To test if the file exists the file could be opened for input (in the normal way using open()) and checked if it has been successfully opened by calling the member function fail(). If the check fails (returns true) the file does not exist, else it does exist. And from this test it may now be decided what to do with the file (create or replace it by opening the new or existing file for output).
In the example the file streams were declared as ifstream for input and ofstream for output. There is also a general file stream class: fstream.
All three - ifstream, ofstream and fstream (and there are more) - allow a second argument when calling the open() member function or the constructor which allows the developer to specify an open mode.
/* File: genFreq.cpp; Mode: C++; Tab-width: 3; Author: Simon Flannery; */
#include
#include
#include
namespace std { }
using namespace std;
int main(int argc, char** argv) {
cout << "File Frequency Table Generator." << endl;
if (argc < 2)
{
cout << "No Input File.";
return 0; // No file, Exit.
}
int intFreq[256] = {0};
char cGet;
char* charDefn[] = {”NUL”, “SOH”, “STX”, “ETX”, “EOT”, “ENQ”, “ACK”, “BEL”,
“BS “, “HT “, “LF “, “VT “, “FF “, “CR “, “SO “, “SI “,
“DLE”, “DC1″, “DC2″, “DC3″, “DC4″, “NAK”, “SYN”, “ETB”,
“CAN”, “EM “, “SUB”, “ESC”, “FS “, “GS “, “RS “, “US ” };
fstream inFile; inFile.open(argv[1], ios::binary | ios::in);
if (inFile.fail())
{
cout << "Input File Error." << endl;
return 0; // File error, Exit.
}
while (inFile.get(cGet))
{
intFreq[int(cGet)]++;
}
inFile.close();
ofstream outFile; outFile.open("out.txt");
if (outFile.fail())
{
cout << "Output File Error." << endl;
return 0; // File error, Exit.
}
for (int i = 0; i < 256; ++i)
{
if (i < 32)
{
outFile << setw(16) << charDefn[i];
}
else
{
outFile << setw(8) << char(i) << setw(8) << " ";
}
outFile << setw(8) << i << setw(8) << intFreq[i] << endl;
}
outFile.close();
cout << "Successful Runnel." << endl;
return 0;
}
The main visible effect of Opening an ASCII file in binary mode is that the new line character ‘\n’ is not automatically mapped as both the new line character and the appropriate system end-of-line sequence. For example, just LF on Unix, CRLF on DOS and CR on Macintosh. “Binary mode is not supposed to suddenly give you a bitstream…” - DJGPP.
The optional ios mode can be:
Mode Meaning
ios::app Append output at end of file.
ios::ate Goto to EOF when opened.
ios::binary Open the file in binary mode.
ios:in Open the file for reading.
ios::out Open file for writing.
ios::trunc Overwrite the existing file.
If the ios mode is not any of the following combination of flags shown in the table then the open method fails.
A summary of some useful file stream control functions:
i/o fstream::open(char*) loads the file name (from relative or absolute path).
istream::getline(char*, int, char) will extract consecutive characters from the istream until either the integer limit - 1 is reached, the delimiter character is found, or the end of the istream is reached. The extracted characters are stored in the given cstring followed by the null terminator. If the delimiter is found, it is extracted but not stored.
Remember that all the overloaded istream::get() functions, neither extracts nor stores the delimiter character.
i/o fstream::close(). It’s polite to close the file i/o stream when all related processing is finished.
i/o fstream::fail() returns true if the file has failed to load and is normally called immediately after a call to open().
i/o fstream::is_open() returns true if the file i/o stream is currently open, else false when the file stream is closed (or failed to open).
ifstream::eof() is an abbreviation for “End of File”. Returns true when the end of the file stream has been reached. Most commonly used to terminate a control loop.
Another common method when testing for the end of a file stream is to test for negative one (-1). Note that this is the number -1 and not the characters ‘-’ and ‘1′. Although this method will almost work in all cases, the -1 end of file marker is not a universal agreement across all platforms! One known platform has the end of file marker as -42. This is why both the member function eof() and the macro EOF exists, which tries to cater for a wide range of platforms. So use ‘em and not -1.
A very common question amongst beginners (and some graduates) is “How can I open a file and insert a new line between two other existing lines?” In short, it is not possible. It must first be understood that this is not a limitation of C++ or any operation system - it’s a hardware limitation. Since files (all the little bits and bytes) are stored consecutively (normally on a disk) there is no “space” left to insert the new line nor a “link” pointing to a new disk cluster and back again. However, the long answer, it can be done. First load the file and all contents (possibly into memory), then rewrite the whole file and the new line where desired.
Dissecting Input Example (Date Validation)
When programming, and when dealing with unpredictable users and their input, an important skill is the ability to separate (or dissect) data into constituent pieces of information.
/* File: getDate.cpp; Mode: C++; Tab-width: 3; Author: Simon Flannery; */
#include
namespace std {}
using namespace std;
/* Return users answer. */
bool booleanResponse(const char*);
/* Validates a char array for a date Representation. */
bool isDate(const char*);
int main() { // Main construct.
char buffer[101];
const int LF = ‘\n’; // New Line, or Line Feed.
do {
cout << "Please Enter a Valid Date: ";
cin.get(buffer, 101, LF); cin.get(); // Get that Last LF!
if (isDate(buffer)) {
cout << "\nThe date " << buffer << " is Valid." << endl;
} else {
cout << "\nThe date " << buffer << " is invalid!" << endl;
}
} while (booleanResponse("\nDo you wish to Enter Another Date? "));
return 0;
} // main.
bool booleanResponse(const char* prompt) {
const int LF = '\n'; // New Line, or Line Feed.
bool result = false;
char input;
do {
cout << prompt;
cin.get(input);
while (cin.get() != LF); // Clear Buffer.
input |= 0x20; // 32. Bit "or" convert.
} while (input != 'y' && input != 'n');
cout << endl;
if (input == 'y') { result = true; }
return result; // Else false.
}
bool isDate(const char *_s) {
const char* p = _s;
int element = 0, date[3] = {0};
int lastDayOfMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
if (!_s) { return false; }
while (*p) {
if (*p >= '0' && *p <= '9') {
date[element] *= 10;
date[element] += *p - ‘0′;
} else {
if (*p == '/') {
element++;
} else {
return false;
}
}
if (element > 2) { return false; }
p++;
}
if (!date[1] || date[1] > 12) { return false; } // Month range check.
// Leap Year check.
if ((date[2] % 4 == 0) && date[1] == 2 && date[0] == 29) { return true; }
// Date range check.
if (!date[0] || date[0] > lastDayOfMonth[date[1] - 1]) { return false; }
return true;
}
The preceding example requests the user to enter a formatted date in the form dd:mm:yy for validation. The isDate() function illustrates a robust procedure for validating and dissecting the given ASCII cstring.
Black-Box Input and Output Testing for isDate().
Input. Output.
Hello World. Invalid.
01/3/2002 Valid.
01/3/10034 Valid (and very future).
01/3 Valid (any year).
29/2/04 Valid
29/02/03 Invalid.
r2/d2/89 Invalid