Reading: Deitel & Deitel, Chap. 11.1 - 11.4, 14.1 - 14.6
Input from the keyboard
The input stream operator >> is used to read data
from the keyboard into your program. Note that the input stream
is always characters, but this operator ``knows'' how to convert to
the appropriate type. For example:
double f;
cin >> f;
The program waits until the user
enters some characters, which hopefully represent a floating
point number. If the user enters the five character string
34.56, the program converts this to the internal representation
of a floating point number with the value 34.56.
The input stream operator has some features which can be helpful or annoying, depending on what you are trying to do. First, input stream operators ignore whitespace characters: space (ASCII 32), tab (ASCII 9), and newline (ASCII 10). If the user types four spaces, return, a tab, and then 34.56, the statement above will read and ignore everything before the 3.
When used to input integer or floating point numbers, input stream operators cannot ignore other non-numeric characters. If the first non-whitespace character read is not a digit (or, for floats and doubles, a decimal point), the statement will fail, and the value in f will be unchanged. (In this example, it remains uninitialized.) Once an input statement fails, all subsequent input statements for the same input stream will also fail. Thus, a program such as this one can enter an infinite loop if the user types a letter in place of a number for input:
int x = 0, sum = 0;
while (x != -1) {
cout << "Enter a number or -1 to end: ";
cin >> x;
if (x != -1) sum += x;
}
As you know, the >> operator can also read strings (arrays of
chars) properly, but it too ignores all whitespace characters. Any
leading whitespace characters are read and ignored. Once it finds a
non-whitespace character, it continues reading characters until the
next whitespace character is read. A null
character '\0' (ASCII 0) is automatically appended to the
end of the string.
Note that you cannot read data into other types of arrays. The following code will generate a compiler error.
int n[5]; // an array of 5 integers cin >> n; // doesn't work
Buffering input
When the user types input from the keyboard, not all of the characters typed are necessarily read by the input statement that requested the typing. The remaining characters are stored in an input buffer associated with cin, where they will be read by subsequent input statements.
Consider the following example:
int i; float f; char c; char s[80]; cin >> i; cin >> f;
Now let's consider a different set of cin statements with the same user input, 231.4:
cin >> c; cin >> i; cin >> s;
31.4\n. At the second statement,
the characters 31 are read, the value 31 is stored in i,
and the input buffer is left with .4\n. At the third statement,
we read into a character array, so all characters up to the first whitespace
character are read in: the first three characters in s become
'.', '4', and '\0', and the input buffer contains
only \n.
Here is an example involving only string input.
char s[80]; cin >> s;
The quick brown fox,
the input stream operator will only read up to the first
whitespace character. Thus the contents of s will be:
U means undefined.
The input buffer would then contain:
quick brown fox\n (with one space before quick)
If the program then executed another statement
cin >> s;
the next characters in the input buffer would
be read. The space after The would be read and ignored, and
the array s would now contain the word quick. The
next time a cin statement is encountered, it would pick up
with the space after quick.
It often happens that you need to read in an entire line of input including whitespace. The clever student might try to do this by reading a character at a time like this:
// bad code to read an entire line including whitespaces
char s[80];
char c = ' '; // initialize variables
int i = 0;
while (c != '\n') {
cin >> c;
s[i] = c;
++i;
}
s[i-1] = '\0'; // replace newline char with null char
but this does not work for two reasons. First, the statement
cin >> c;
skips whitespaces. Thus the words will all be bunched together with
no spaces between them like this,
Thequickbrownfox. More importantly, the program will
never detect the newline character, so the loop will continue forever.
(Remember that if a program is stuck, you can kill
it with CTRL-C.)
Other ifstream class member functions
The input stream cin has a number of member functions. We will describe a few of the most commonly used ones in this section.1One way to solve the problem above is to use the member function int cin.get(char &c), which reads a single character at a time and does not skip whitespace characters. Thus, the following program would work as intended (as long as the line has fewer than 80 characters):
char s[80];
char c;
int i = 0;
cin.get(c); // priming read before start of loop
while (c != '\n') {
s[i] = c;
i++;
cin.get(c);
}
s[i] = '\0'; // append a null char at the end
This code also has a better loop structure than the first example. Loops written for many different applications, but especially user input, benefit from placing the first read statement just before the loop test (called a priming read), and placing the second read statement at the end of the loop (which is just before the loop goes back to do its test again). In this way, the data read in is always tested by the while statement immediately after it is read.
Another member function of ifstream is int getline(char
*s, int n), where s is a character array and n is the
length of this array. This function will read a string (including
whitespaces) until it either reads a newline character or until
characters have been read. If it reads a newline character,
it discards it. It automatically appends a terminal null character to
the end of s, which is why it reads only
characters
rather than
characters. (getline can also take a third
argument, a delimiter or stopping character to search for instead of \n,
which is the default.)
Here is the simple way to read a line of text from the terminal including white spaces.
char s[80]; cin.getline(s,80);
Mixing cin >> statements with getline statements can
produce confusing results. For example:
int day; char s[80]; cout << "Enter day of month: "; cin >> day; cout << "Enter text string: "; cin.getline(s, 80);
When the first cin statement is executed, the user types in a number and presses the return key. The statement stores the number into day, and the input buffer still holds the newline character. The getline function call, which is supposed to read until either 79 characters have been read or it sees a newline, reads in this newline character and immediately stops. The result is the user does not get to type in a line of text, and s contains a null character in position 0.
To avoid this, we could add a second ``dummy'' call to getline
to absorb that extra newline character. A more elegant way is to call the
ignore member function in between the two input statements, like
this:
cin.ignore(80, '\n');
This function will read and throw out characters until either 80
characters have been ignored or the newline character is seen. (By
changing the arguments you can have it look for a different number of
characters, or a stopping character other than newline.)
Exercise 1: Write a program that prompts the user to enter a
line of text from the keyboard (possibly including whitespaces).
Your program should read in the line, convert all of the lower case
letters in the line to upper case, and then print the line to the
screen. For example, if the user entered:
The TV cost $250.
Your program would write
THE TV COST $250.
Note that there were three spaces between TV and cost, so
your program should write three spaces. Try it once using getline once using get. Which one is easier?
Output to the terminal
The output stream operator << is used to print
data to the screen.
Note that what
is actually written to the screen is always characters. The
operator << ``knows'' how to convert
from data types like int and float to characters.
For example, when this code fragment is executed:
float f = 34.56;
cout << f << endl;
five characters, the digit '3' (ASCII 51), the digit '4' (ASCII 52),
a period (ASCII 46), the digit '5' (ASCII 53) and the digit '6'
(ASCII 54) are written to the screen.
The endl causes a newline character (ASCII 10) to be written. In addition, it causes the output buffer to be flushed. To be more efficient, output statements first write data to a buffer, and only at certain times is this buffer emptied and the output actually written. One of these times is when an endl is encountered. Another is when a cin input statement follows a cout statement.
This is why you sometimes don't see the results of cout statements in your program if it is stuck in an infinite loop, or it crashes in the middle. In cases like these, you can either make sure you use endl frequently, or you can instead write to cerr, which is similar to cout but does not buffer its output.
Like the class to which cin belongs, there are some member functions associated with the ostream class, of which cout is an instance, that can be used to make your output neater.
The function cout.width(w) sets a minimum width for the next item to be written. If the number of characters to be printed is less than this value, additional spaces are printed. The items are right justified so this makes it easy to print a table of numbers with the columns aligned. Here is a simple example:
#include <iostream>
using namespace std;
//prints first 5 powers of 2, 3, 4
int main()
{
int n2 = 2, n3 = 3, n4 = 4;
int i;
for(i = 1; i < 6; ++i) {
cout.width(8);
cout << n2;
cout.width(8);
cout << n3;
cout.width(8);
cout << n4;
cout << endl;
n2 *= 2;
n3 *= 3;
n4 *= 4;
}
return 0;
}
The output of this program looks like this:
2 3 4
4 9 16
8 27 64
16 81 256
32 243 1024
You can also set the precision of real numbers with the function cout.precision(p) where p is the number of digits to the right of the decimal point. The default is 6. Here is an example:
#include <iostream>
using namespace std;
int main()
{
double d;
d = 2.0 / 3.0;
cout << d << endl; // default precision
cout.precision(2);
cout << d << endl;
cout.precision(8);
cout << d << endl;
return 0;
}
Here is the output of this program.
0.666667 0.67 0.66666667
The output stream operator will print strings in the expected manner; that is, it will print an array of characters (including spaces and newline characters) up to the first null character.
However, you cannot print other types of arrays. If you create
an array of integers like this:
int n[6];
and insert some values into it, and then try to print it like this:
cout << n;
the program will display the address of n in hexadecimal.
Exercise 2: The function sqrt takes a variable of type double and returns a value of type double which is the square
root of its argument. Write a program which prints the square roots
of 2, 3, and 5 to precisions of 0 to nine significant digits to the
right of the decimal. Note that you must include the header file
<math.h> to use sqrt.
Here is what your output should look like.
Root of 2 3 5
------------------------------------
1 2 2
1.4 1.7 2.2
1.41 1.73 2.24
1.414 1.732 2.236
1.4142 1.7321 2.2361
1.41421 1.73205 2.23607
1.414214 1.732051 2.236068
1.4142136 1.7320508 2.236068
1.41421356 1.73205081 2.23606798
Your program should have a nested loop and should not be more than
about 25 lines.
Other output functions and manipulators
The library <iomanip> contains a number of other features to
further customize input and output behavior. For example, you may
already be familiar with the setw and setprecision
manipulators in this library, which perform the same functions as the
width and precision member functions described above. You
can find a discussion of these advanced I/O options in sections
11.6 - 11.9 of the Deitel & Deitel text.
Files
Most computer applications have to read stored data from files and write data to files. There are two data types for files defined in the header file fstream.2An ifstream is an input file, that is, a file from which the program will read data, and an ofstream is an output file, that is, a file to which the program will write data.
Before a file can be used, it must be opened. Here is the C++ syntax to open an input file.
ifstream file ;
file.open(filename); 3
The variable filename is a null terminated array of characters which refers to a file on your system. In order for a file to be opened for input, it must already exist, and you must have read permission for it. If this is not the case, the open will fail. If the open fails, the value of the file variable will be NULL. You should always check for this and take appropriate action if the open has failed.
Here is an example of some code which opens a file called myfile for input.
ifstream in;
in.open("c:\\temp\\myfile.txt");
if (in == NULL) {
cerr << "Error, could not open the file myfile" << endl;
exit(0);
}
The exit(0) function immediately terminates your program.
A filename can be just the name of a file, or it can include a full pathname, as in the example above. If it is only the name of a file, the program will look for the file in the working directory (normally the directory where the program is being run).
Alert: On a Windows system, subdirectory names in a path are
separated with the backslash \ character. To represent a
backslash character inside a string, you must type two backslashes in
a row; a single backslash is used for special characters such as
newline (\n), the null character (\0), etc. But, if you
are reading in a file name from the keyboard, the user doesn't need to
type double backslashes; the input functions take care of it.
Output files are opened in the same way as input files. However, they do not need to exist prior to being opened. If a file of that name does not exist, it will be automatically created, and if it does already exist, the contents will be erased and overwritten (without warning!)
Once a file has been opened for input, you can use the >> operator to
read from the file, just as you did with cin. (In fact, cin
is just an instance of an ifstream.) You can also use all of the
other input functions mentioned above such as get and getline.
Likewise, you can use the
<< operator to write to a file that has been opened for output,
You can also use all of the other output functions such as width
and precision. To use the functions, just replace cin
or cout with the name of the ifstream or ofstream object.
When you are finished with a file, you should close it with the close() member function; although all open files are automatically closed when a program terminates.
Typically when you open a file for input, you do not know how many lines there are or how many characters there are, and you want to read the entire file. One easy way to test if you have reached the end of an input file is by testing the name of the ifstream object itself. When the end of file is reached, its value becomes NULL.
Alert: In C++ end-of-file is not sensed until after a read function call fails because the end of the file has been reached. Thus, you must always attempt to read from the file before testing for end-of-file. One good way to do this is to structure your reading loop with a priming read, as was described earlier:
input statement
while (ifstreamobject != NULL) {
all statements which process the most recent input
input statement
}
Here is a complete program which takes two file names
typed in by the user. It opens the input file name and makes an
exact duplicate of the file in the output file name. It shows
the use of a priming read and testing the ifstream object for the
reading loop. (It is in /dept/cs/cs2/download/copyfile.cpp)
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
ifstream in; // the input file
ofstream out; // the output file
char inname[80], outname[80]; // holds file names
cout << "Input file? ";
cin >> inname;
in.open(inname);
if (in == NULL) {
cerr << "Error, could not open the file " << inname << endl;
exit(0);
}
cout << "Output file? ";
cin >> outname;
out.open(outname);
if (out == NULL) {
cerr << "Error, could not open the file " << outname << endl;
exit(0);
}
char c;
in.get(c); // can't use in >> c; -- don't want to skip whitespace
while (in != NULL) {
out << c;
in.get(c);
}
return 0;
}
There is an easier way to test for end of file, if you can structure your reading loop to take advantage of it. Some input functions, including get and getline, return NULL when they attempt to read at the end of a file, so you can embed the function call inside a while statement and test this return value. If the read succeeds, the function returns a non-zero (true) value, and the statements inside the while loop are executed. If it fails, the loop ends. Here is an example. Pay particular attention to the structure of the while statement.
ifstream infile;
infile.open("c:\\temp\\myfile.txt");
if (infile == NULL) {
cerr << "Error, could not open myfile" << endl;
exit(0);
}
char line[81];
while (infile.getline(line,80) != NULL) {
// do whatever processing on the line
}
infile.close();
This program reads one line at a time from the file myfile, continuing to loop as long as there are more lines to be read, and then terminates. Note: if a line is encountered with more than 80 characters in it, the first 80 characters would be read and processed first, and the remaining characters on the line would be processed on the next pass through the loop.
Exercise 3: Rewrite the code that you wrote for Exercise 1
so that it prompts the user for two file names. The first is the
input file and the second is the output file. The input file
should consist of several lines of text including whitespaces.
Your program should open this file, read it one line at a time,
convert all lower case letters to upper case and write the line to
the output file. Note that the number of characters written to the
output file should be identical to the number of characters in the
input file.