Next, let’s examine text file input. It’s based on console input, which has many elements. So let’s begin with a summary those elements:
• You must include the iostream
header file.
• The iostream
header file defines an istream
class for handling input.
• The iostream
header file declares an istream
variable, or object, called cin
.
• You must account for the std
namespace; for example, you can use the using
directive or the std::
prefix for elements such as cin
.
• You can use cin
with the >>
operator to read a variety of data types.
• You can use cin
with the get()
method to read individual characters and with the getline()
method to read a line of characters at a time.
• You can use cin
with methods such as eof()
and fail()
to monitor the success of an input attempt.
• The object cin
itself, when used as a test condition, is converted to the Boolean value true
if the last read attempt succeeded and to false
otherwise.
File input parallels this very closely:
• You must include the fstream
header file.
• The fstream
header file defines an ifstream
class for handling input.
• You need to declare one or more ifstream
variables, or objects, which you can name as you please, as long as you respect the usual naming conventions.
• You must account for the std
namespace; for example, you can use the using
directive or the std::
prefix for elements such as ifstream
.
• You need to associate a specific ifstream
object with a specific file; one way to do so is to use the open()
method.
• When you’re finished with a file, you should use the close()
method to close the file.
• You can use an ifstream
object with the >>
operator to read a variety of data types.
• You can use an ifstream
object with the get()
method to read individual characters and with the getline()
method to read a line of characters at a time.
• You can use an ifstream
object with methods such as eof()
and fail()
to monitor the success of an input attempt.
• An ifstream
object itself, when used as a test condition, is converted to the Boolean value true
if the last read attempt succeeded and to false
otherwise.
Note that although the iostream
header file provides a predefined istream
object called cin
, you have to declare your own ifstream
object, choosing a name for it and associating it with a file. Here’s how you declare such objects:
ifstream inFile; // inFile an ifstream object
ifstream fin; // fin an ifstream object
Here’s how you can associate them with particular files:
inFile.open("bowling.txt"); // inFile used to read bowling.txt file
char filename[50];
cin >> filename; // user specifies a name
fin.open(filename); // fin used to read specified file
Note that the open()
method requires a C-style string as its argument. This can be a literal string or a string stored in an array.
Here’s how you can use these objects:
double wt;
inFile >> wt; // read a number from bowling.txt
char line[81];
fin.getline(line, 81); // read a line of text
The important point is that after you’ve declared an ifstream
object and associated it with a file, you can use it exactly as you would use cin
. All the operations and methods available to cin
are also available to ifstream
objects, such as inFile
and fin
in the preceding examples.
What happens if you attempt to open a non-existent file for input? This error causes subsequent attempts to use the ifstream
object for input to fail. The preferred way to check whether a file was opened successfully is to use the is_open()
method. You can use code like this:
inFile.open("bowling.txt");
if (!inFile.is_open())
{
exit(EXIT_FAILURE);
}
The is_open()
method returns true
if the file was opened successfully, so the expression !inFile.is_open()
is true
if the attempt fails. The exit()
function is prototyped in the cstdlib
header file, which also defines EXIT_FAILURE
as an argument value used to communicate with the operating system. The exit()
function terminates the program.
The is_open()
method is relatively new to C++. If your compiler doesn’t support it, you can use the older good()
method instead. As Chapter 17 discusses, good()
doesn’t check quite as extensively as is_open()
for possible problems.
The program in Listing 6.16 opens a file specified by the user, reads numbers from the file, and reports the number of values, their sum, and their average. It’s important that you design the input loop correctly, and the following “Program Notes” section discusses this in more detail. Notice that this program benefits greatly from using if
statements.
// sumafile.cpp -- functions with an array argument
#include <iostream>
#include <fstream> // file I/O support
#include <cstdlib> // support for exit()
const int SIZE = 60;
int main()
{
using namespace std;
char filename[SIZE];
ifstream inFile; // object for handling file input
cout << "Enter name of data file: ";
cin.getline(filename, SIZE);
inFile.open(filename); // associate inFile with a file
if (!inFile.is_open()) // failed to open file
{
cout << "Could not open the file " << filename << endl;
cout << "Program terminating.
";
exit(EXIT_FAILURE);
}
double value;
double sum = 0.0;
int count = 0; // number of items read
inFile >> value; // get first value
while (inFile.good()) // while input good and not at EOF
{
++count; // one more item read
sum += value; // calculate running total
inFile >> value; // get next value
}
if (inFile.eof())
cout << "End of file reached.
";
else if (inFile.fail())
cout << "Input terminated by data mismatch.
";
else
cout << "Input terminated for unknown reason.
";
if (count == 0)
cout << "No data processed.
";
else
{
cout << "Items read: " << count << endl;
cout << "Sum: " << sum << endl;
cout << "Average: " << sum / count << endl;
}
inFile.close(); // finished with the file
return 0;
}
To use the program in Listing 6.16, you first have to create a text file that contains numbers. You can use a text editor, such as the text editor you use to write source code, to create this file. Let’s assume that the file is called scores.txt
and has the following contents:
18 19 18.5 13.5 14
16 19.5 20 18 12 18.5
17.5
The program has to be able to find the file. Typically, unless your input includes a pathname with the file, the program will look in the same folder or directory that contains the executable file.
A Windows text file uses the carriage return character followed by a linefeed character to terminate a line of text. (The usual C++ text mode translates this combination to newline character when reading a file and reverses the translation when writing to a file.) Some text editors, such as the Metrowerks CodeWarrior IDE editor, don’t automatically add a this combination to the final line. Therefore, if you use such an editor, you need to press the Enter key after typing the final text and before exiting the file.
Here’s a sample run of the program in Listing 6.16:
Enter name of data file: scores.txt
End of file reached.
Items read: 12
Sum: 204.5
Average: 17.0417
Instead of hard-coding a filename, the program in Listing 6.16 stores a user-supplied name in the character array filename
. Then the array is used as an argument to open()
:
inFile.open(filename);
As discussed earlier in this chapter, it’s vital to test whether the attempt to open the file succeeded. Here are a few of the things that might go wrong: The file might not exist, the file might be located in another directory or file folder, access might be denied, and the user might mistype the name or omit a file extension. Many a beginner has spent a long time trying to figure what’s wrong with a file-reading loop when the real problem was that the program didn’t open the file. Testing for file-opening failure can save you such misspent effort.
You need to pay close attention to the proper design of a file-reading loop. There are several things to test for when reading from a file. First, the program should not try to read past the EOF. The eof()
method returns true
if the most recent attempt to read data ran into the EOF. Second, the program might encounter a type mismatch. For instance, Listing 6.16 expects a file containing only numbers. The fail()
method returns true
if the most recent read attempt encountered a type mismatch. (This method also returns true
if the EOF is encountered.) Finally, something unexpected may go wrong—for example, a corrupted file or a hardware failure. The bad()
method returns true
if the most recent read attempt encountered such a problem. Rather than test for these conditions individually, it’s simpler to use the good()
method, which returns true
if nothing when wrong:
while (inFile.good()) // while input good and not at EOF
{
...
}
Then, if you like, you can use the other methods to determine exactly why the loop terminated:
if (inFile.eof())
cout << "End of file reached.
";
else if (inFile.fail())
cout << "Input terminated by data mismatch.
";
else
cout << "Input terminated for unknown reason.
";
This code comes immediately after the loop so that it investigates why the loop terminated. Because eof()
tests just for the EOF and fail()
tests for both the EOF and type mismatch, this code tests for the EOF first. That way, if execution reaches the else if
test, the EOF has already been excluded, so a true
value for fail()
unambiguously identifies type mismatch as the cause of loop termination.
It’s particularly important that you understand that good()
reports on the most recent attempt to read input. That means there should be an attempt to read input immediately before applying the test. A standard way of doing that is to have one input statement immediately before the loop, just before the first execution of the loop test, and a second input statement at the end of the loop, just before subsequent executions of the loop test:
// standard file-reading loop design
inFile >> value; // get first value
while (inFile.good()) // while input good and not at EOF
{
// loop body goes here
inFile >> value; // get next value
}
You can condense this somewhat by using the fact that the following expression evaluates to inFile
and that inFile
, when placed in a context in which a bool
value is expected, evaluates to inFile.good()
—that is, to true
or false
:
inFile >> value
Thus, you can replace the two input statements with a single input statement used as a loop test. That is, you can replace the preceding loop structure with this:
// abbreviated file-reading loop design
// omit pre-loop input
while (inFile >> value) // read and test for success
{
// loop body goes here
// omit end-of-loop input
}
This design still follows the precept of attempting to read before testing because to evaluate the expression inFile >> value
, the program first has to attempt to read a number into value
.
Now you know the rudiments of file I/O.
Programs and programming become more interesting when you introduce statements that guide the program through alternative actions. (Whether this also makes the programmer more interesting is a point you may wish to investigate.) C++ provides the if
statement, the if else
statement, and the switch
statement as means for managing choices. The C++ if
statement lets a program execute a statement or statement block conditionally. That is, the program executes the statement or block if a particular condition is met. The C++ if else
statement lets a program select from two choices which statement or statement block to execute. You can append additional if else
statements to such a statement to present a series of choices. The C++ switch
statement directs the program to a particular case in a list of choices.
C++ also provides operators to help in decision making. Chapter 5 discusses the relational expressions, which compare two values. The if
and if else
statements typically use relational expressions as test conditions. By using C++’s logical operators (&&
, ||
, and !
), you can combine or modify relational expressions to construct more elaborate tests. The conditional operator (?:
) provides a compact way to choose from two values.
The cctype
library of character functions provides a convenient and powerful set of tools for analyzing character input.
Loops and selection statements are useful tools for file I/O, which closely parallels console I/O. After you declare ifstream
and ofstream
objects and associate them with files, you can use these objects in the same manner you use cin
and cout
.
With C++’s loops and decision-making statements, you have the tools for writing interesting, intelligent, and powerful programs. But we’ve only begun to investigate the real powers of C++. Next, we’ll look at functions.
1. Consider the following two code fragments for counting spaces and newlines:
// Version 1
while (cin.get(ch)) // quit on eof
{
if (ch == ' ')
spaces++;
if (ch == '
')
newlines++;
}
// Version 2
while (cin.get(ch)) // quit on eof
{
if (ch == ' ')
spaces++;
else if (ch == '
')
newlines++;
}
What advantages, if any, does the second form have over the first?
2. In Listing 6.2, what is the effect of replacing ++ch
with ch+1
?
3. Carefully consider the following program:
#include <iostream>
using namespace std;
int main()
{
char ch;
int ct1, ct2;
ct1 = ct2 = 0;
while ((ch = cin.get()) != '$')
{
cout << ch;
ct1++;
if (ch = '$')
ct2++;
cout << ch;
}
cout <<"ct1 = " << ct1 << ", ct2 = " << ct2 << "
";
return 0;
}
Suppose you provide the following input, pressing the Enter key at the end of each line:
Hi!
Send $10 or $20 now!
What is the output? (Recall that input is buffered.)
4. Construct logical expressions to represent the following conditions:
a. weight
is greater than or equal to 115 but less than 125.
b. ch
is q
or Q
.
c. x
is even but is not 26
.
d. x
is even but is not a multiple of 26
.
e. donation
is in the range 1,000–2,000 or guest
is 1
.
f. ch
is a lowercase letter or an uppercase letter. (Assume, as is true for ASCII, that lowercase letters are coded sequentially and that uppercase letters are coded sequentially but that there is a gap in the code between uppercase and lowercase.)
5. In English, the statement “I will not not speak” means the same as “I will speak.” In C++, is !!x
the same as x
?
6. Construct a conditional expression that is equal to the absolute value of a variable. That is, if a variable x
is positive, the value of the expression is just x
, but if x
is negative, the value of the expression is -x
, which is positive.
7. Rewrite the following fragment using switch
:
if (ch == 'A')
a_grade++;
else if (ch == 'B')
b_grade++;
else if (ch == 'C')
c_grade++;
else if (ch == 'D')
d_grade++;
else
f_grade++;
8. In Listing 6.10, what advantage would there be in using character labels, such as a
and c
, instead of numbers for the menu choices and switch cases? (Hint: Think about what happens if the user types q
in either case and what happens if the user types 5
in either case.)
9. Consider the following code fragment:
int line = 0;
char ch;
while (cin.get(ch))
{
if (ch == 'Q')
break;
if (ch != '
')
continue;
line++;
}
Rewrite this code without using break
or continue
.
1. Write a program that reads keyboard input to the @
symbol and that echoes the input except for digits, converting each uppercase character to lowercase, and vice versa. (Don’t forget the cctype
family.)
2. Write a program that reads up to 10 donation values into an array of double
. (Or, if you prefer, use an array
template object.) The program should terminate input on non-numeric input. It should report the average of the numbers and also report how many numbers in the array are larger than the average.
3. Write a precursor to a menu-driven program. The program should display a menu offering four choices, each labeled with a letter. If the user responds with a letter other than one of the four valid choices, the program should prompt the user to enter a valid response until the user complies. Then the program should use a switch to select a simple action based on the user’s selection. A program run could look something like this:
Please enter one of the following choices:
c) carnivore p) pianist
t) tree g) game
f
Please enter a c, p, t, or g: q
Please enter a c, p, t, or g: t
A maple is a tree.
4. When you join the Benevolent Order of Programmers, you can be known at BOP meetings by your real name, your job title, or your secret BOP name. Write a program that can list members by real name, by job title, by secret name, or by a member’s preference. Base the program on the following structure:
// Benevolent Order of Programmers name structure
struct bop {
char fullname[strsize]; // real name
char title[strsize]; // job title
char bopname[strsize]; // secret BOP name
int preference; // 0 = fullname, 1 = title, 2 = bopname
};
In the program, create a small array of such structures and initialize it to suitable values. Have the program run a loop that lets the user select from different alternatives:
a. display by name b. display by title
c. display by bopname d. display by preference
q. quit
Note that “display by preference” does not mean display the preference member; it means display the member corresponding to the preference number. For instance, if preference is 1
, choice d
would display the programmer’s job title. A sample run may look something like the following:
Benevolent Order of Programmers Report
a. display by name b. display by title
c. display by bopname d. display by preference
q. quit
Enter your choice: a
Wimp Macho
Raki Rhodes
Celia Laiter
Hoppy Hipman
Pat Hand
Next choice: d
Wimp Macho
Junior Programmer
MIPS
Analyst Trainee
LOOPY
Next choice: q
Bye!
5. The Kingdom of Neutronia, where the unit of currency is the tvarp, has the following income tax code:
First 5,000 tvarps: 0% tax
Next 10,000 tvarps: 10% tax
Next 20,000 tvarps: 15% tax
Tvarps after 35,000: 20% tax
For example, someone earning 38,000 tvarps would owe 5,000 × 0.00 + 10,000 × 0.10 + 20,000 × 0.15 + 3,000 × 0.20, or 4,600 tvarps. Write a program that uses a loop to solicit incomes and to report tax owed. The loop should terminate when the user enters a negative number or non-numeric input.
6. Put together a program that keeps track of monetary contributions to the Society for the Preservation of Rightful Influence. It should ask the user to enter the number of contributors and then solicit the user to enter the name and contribution of each contributor. The information should be stored in a dynamically allocated array of structures. Each structure should have two members: a character array (or else a string
object) to store the name and a double
member to hold the amount of the contribution. After reading all the data, the program should display the names and amounts donated for all donors who contributed $10,000 or more. This list should be headed by the label Grand Patrons. After that, the program should list the remaining donors. That list should be headed Patrons. If there are no donors in one of the categories, the program should print the word “none.” Aside from displaying two categories, the program need do no sorting.
7. Write a program that reads input a word at a time until a lone q
is entered. The program should then report the number of words that began with vowels, the number that began with consonants, and the number that fit neither of those categories. One approach is to use isalpha()
to discriminate between words beginning with letters and those that don’t and then use an if
or switch
statement to further identify those passing the isalpha()
test that begin with vowels. A sample run might look like this:
Enter words (q to quit):
The 12 awesome oxen ambled
quietly across 15 meters of lawn. q
5 words beginning with vowels
4 words beginning with consonants
2 others
8. Write a program that opens a text file, reads it character-by-character to the end of the file, and reports the number of characters in the file.
9. Do Programming Exercise 6 but modify it to get information from a file. The first item in the file should be the number of contributors, and the rest of the file should consist of pairs of lines, with the first line of each pair being a contributor’s name and the second line being a contribution. That is, the file should look like this:
4
Sam Stone
2000
Freida Flass
100500
Tammy Tubbs
5000
Rich Raptor
55000
13.58.209.201