The End-of-File Condition

As Listing 5.17 shows, using a symbol such as # to signal the end of input is not always satisfactory because such a symbol might be part of legitimate input. The same is true of other arbitrarily chosen symbols, such as @ and %. If the input comes from a file, you can employ a much more powerful technique—detecting the end-of-file (EOF). C++ input facilities cooperate with the operating system to detect when input reaches the end of a file and report that information back to a program.

At first glance, reading information from files seems to have little to do with cin and keyboard input, but there are two connections. First, many operating systems, including Unix, Linux, and the Windows Command Prompt mode, support redirection, which enables you to substitute a file for keyboard input. For example, suppose in Windows you have an executable program called gofish.exe and a text file called fishtale. In that case, you can give this command line in the command prompt mode:

gofish <fishtale

This causes the program to take input from the fishtale file instead of from the keyboard. The < symbol is the redirection operator for both Unix and the Windows Command Prompt mode.

Second, many operating systems allow you to simulate the EOF condition from the keyboard. In Unix you do so by pressing Ctrl+D at the beginning of a line. In the Windows Command Prompt mode, you press Ctrl+Z and then press Enter anywhere on the line. Some implementations of C++ support similar behavior even though the underlying operating system doesn’t. The EOF concept for keyboard entry is actually a legacy of command-line environments. However, Symantec C++ for the Mac imitates Unix and recognizes Ctrl+D as a simulated EOF. Metrowerks Codewarrior recognizes Ctrl+Z in the Macintosh and Windows environments. Microsoft Visual C++, Borland C++ 5.5, and GNU C++ for the PC recognize Ctrl+Z when it’s the first character on a line, but they require a subsequent Enter. In short, many PC programming environment recognize Ctrl+Z as a simulated EOF, but the exact details (anywhere on a line versus first character on a line, Enter key required or not required) vary.

If your programming environment can test for the EOF, you can use a program similar to Listing 5.17 with redirected files and you can use it for keyboard input in which you simulate the EOF. That sounds useful, so let’s see how it’s done.

When cin detects the EOF, it sets two bits (the eofbit and the failbit) to 1. You can use a member function named eof() to see whether the eofbit has been set; the call cin.eof() returns the bool value true if the EOF has been detected and false otherwise. Similarly, the fail() member function returns true if either the eofbit or the failbit has been set to 1 and false otherwise. Note that the eof() and fail() methods report the result of the most recent attempt to read; that is, they report on the past rather than look ahead. So a cin.eof() or cin.fail() test should always follow an attempt to read. The design of Listing 5.18 reflects this fact. It uses fail() instead of eof() because the former method appears to work with a broader range of implementations.


Note

Some systems do not support simulated EOF from the keyboard. Other systems support it imperfectly. If you have been using cin.get() to freeze the screen until you can read it, that won’t work here because detecting the EOF turns off further attempts to read input. However, you can use a timing loop like that in Listing 5.14 to keep the screen visible for a while. Or you can use cin.clear(), as mentioned in Chapters 6 and 17, to reset the input stream.


Listing 5.18. textin3.cpp


// textin3.cpp -- reading chars to end of file
#include <iostream>
int main()
{
    using namespace std;
    char ch;
    int count = 0;
    cin.get(ch);        // attempt to read a char
    while (cin.fail() == false)  // test for EOF
    {
        cout << ch;     // echo character
        ++count;
        cin.get(ch);    // attempt to read another char
    }
    cout << endl << count << " characters read ";
    return 0;
}


Here is sample output from the program in Listing 5.18:

The green bird sings in the winter.<ENTER>
The green bird sings in the winter.
Yes, but the crow flies in the dawn.<ENTER>
Yes, but the crow flies in the dawn.
<CTRL>+<Z><ENTER>
73 characters read

Because I ran the program on a Windows 7 system, I pressed Ctrl+Z and then Enter to simulate the EOF condition. Unix and Linux users would press Ctrl+D instead. Note that in Unix and Unix-like systems, including Linux and Cygwin, Ctrl+Z suspends execution of the program; the fg command lets execution resume.

By using redirection, you can use the program in Listing 5.18 to display a text file and report how many characters it has. This time, we have a program read, echo, and count characters from a two-line file on a Unix system (the $ is a Unix prompt):

$ textin3 < stuff
I am a Unix file. I am proud
to be a Unix file.
48 characters read
$

EOF Ends Input

Remember that when a cin method detects the EOF, it sets a flag in the cin object, indicating the EOF condition. When this flag is set, cin does not read anymore input, and further calls to cin have no effect. For file input, this makes sense because you shouldn’t read past the end of a file. For keyboard input, however, you might use a simulated EOF to terminate a loop but then want to read more input later. The cin.clear() method clears the EOF flag and lets input proceed again. Chapter 17, “Input, Output, and Files,” discusses this further. Keep in mind, however, that in some systems, typing Ctrl+Z effectively terminates both input and output beyond the powers of cin.clear() to restore them.

Common Idioms for Character Input

The following is the essential design of a loop intended to read text a character at a time until EOF:

cin.get(ch);        // attempt to read a char
while (cin.fail() == false)  // test for EOF
{
    ...             // do stuff
    cin.get(ch);    // attempt to read another char
}

There are some shortcuts you can take with this code. Chapter 6 introduces the ! operator, which toggles true to false and vice versa. You can use it to rewrite the while test to look like this:

while (!cin.fail())    // while input has not failed

The return value for the cin.get(char) method is cin, an object. However, the istream class provides a function that can convert an istream object such as cin to a bool value; this conversion function is called when cin occurs in a location where a bool is expected, such as in the test condition of a while loop. Furthermore, the bool value for the conversion is true if the last attempted read was successful and false otherwise. This means you can rewrite the while test to look like this:

while (cin)    // while input is successful

This is a bit more general than using !cin.fail() or !cin.eof() because it detects other possible causes of failure, such as disk failure.

Finally, because the return value of cin.get(char) is cin, you can condense the loop to this format:

while (cin.get(ch))  // while input is successful
{
    ...              // do stuff
}

Here, cin.get(char) is called once in the test condition instead of twice—once before the loop and once at the end of the loop. To evaluate the loop test, the program first has to execute the call to cin.get(ch), which, if successful, places a value into ch. Then the program obtains the return value from the function call, which is cin. Then it applies the bool conversion to cin, which yields true if input worked and false otherwise. The three guidelines (identifying the termination condition, initializing the condition, and updating the condition) are all compressed into one loop test condition.

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

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