Reading from a Text File

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.

Listing 6.16. sumafile.cpp


// 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.


Caution

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

Program Notes

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.

Summary

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.

Chapter Review

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.

Programming Exercises

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

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
13.58.209.201