EXPLORATION 5

image

Simple Input

So far, the Explorations have focused on output. Now it’s time to turn your attention to input. Given that the output operator is <<, what do you expect the input operator to be? ________________

That didn’t take a rocket scientist to deduce, did it? The input operator is >>, the opposite direction of the output operator. Think of the operators as arrows pointing in the direction that information flows: from the stream to variables for input, or from variables to the stream for output.

Listing 5-1 shows a simple program that performs input and output.

Listing 5-1.  Demonstrating Input and Output

#include <iostream>
 
int main()
{
   std::cout << "Enter a number: ";
   int x;
   std::cin >> x;
   std::cout << "Enter another number: ";
   int y;
   std::cin >> y;
 
   int z{x + y};
   std::cout << "The sum of " << x << " and " << y << " is " << z << " ";
}

How many numbers does Listing 5-1 read from the standard input? ________________

Suppose you enter 42 and 21 as the two input values. What do you expect for the output?

_____________________________________________________________

Now run the program and check your prediction. I hope you got 63. Suppose you type the following as input?

42*21

What do you predict will be the output?

_____________________________________________________________

Test your hypothesis. What is the actual output?

_____________________________________________________________

Do you see what happened? If not, try xyz as the input to the program. Try 42-21. Try 42.21.

The program exhibits two distinct behaviors that you have to understand. First, to read an int, the input stream must contain a valid integer. The integer can start with a sign (- or +) but must be all digits after that; no intervening whitespace is allowed. The input operation stops when it reaches the first character that cannot be part of a valid integer (such as *). If at least one digit is read from the input stream, the read succeeds, and the input text is converted to an integer. The read fails if the input stream does not start with a valid integer. If the read fails, the input variable is not modified.

The second behavior is what you discovered in the previous Exploration; uninitialized int variables result in undefined behavior. In other words, if a read fails, the variable contains junk, or worse. When you learn about floating point numbers, for example, you will learn that some bit patterns in an uninitialized floating-point variable can cause a program to terminate. On some specialized hardware, an uninitialized integer can do the same. The moral of the story is that using an uninitialized variable results in undefined behavior. That’s bad. So don’t do it.

Thus, when the input is xyz, both reads fail, and undefined behavior results. You probably see junk values for both numbers. When the input is 42-21, the first number is 42 and the second number is -21, so the result is correct. However, when the input is 42.21, the first number is 42, and the second number is junk, because an integer cannot start with a dot (.).

Once an input operation fails, all subsequent input attempts will also fail, unless you take remedial action. That’s why the program doesn’t wait for you to type a second number if the first one is invalid. C++ can tell you when an input operation fails, so your program can avoid using junk values. Also, you can reset a stream’s error state, so you can resume reading after handling an error. I will cover these techniques in future Explorations. For now, make sure your input is valid and correct.

Some compilers warn you when your program leaves variables uninitialized, but it is best to be safe and initialize every variable, all the time. As you can see, even if the program immediately attempts to store a value in the variable, it might not succeed, which can give rise to unexpected behavior.

Did you think that integers could be so complicated? Surely strings are simpler, because there is no need to interpret them or convert their values. Let’s see if they truly are simpler than integers. Listing 5-2 is similar to Listing 5-1, but it reads text into std::string variables.

Listing 5-2.  Reading Strings

#include <iostream>
#include <string>
 
int main()
{
   std::cout << "What is your name? ";
   std::string name{};
   std::cin >> name;
   std::cout << "Hello, " << name << ", how are you? ";
   std::string response{};
   std::cin >> response;
   std::cout << "Good-bye, " << name << ". I'm glad you feel " << response << " ";
}

Listing 5-2 is clearly not a model of artificial intelligence, but it demonstrates one thing well. Suppose the input is as follows:

Ray Lischner
Fine

What do you expect as the output?

_____________________________________________________________

_____________________________________________________________

_____________________________________________________________

Run the program and test your hypothesis. Were you correct? ________________

Explain.

_____________________________________________________________

_____________________________________________________________

Experiment with different input and try to discern the rules that C++ uses to delimit a string in the input stream. Ready? Go ahead. I’ll wait until you’re done.

Back so soon? How does C++ delimit strings in an input stream?

_____________________________________________________________

_____________________________________________________________

_____________________________________________________________

Any whitespace character (the exact list of whitespace characters depends on your implementation, but typically includes blanks, tabs, newlines, and the like) ends a string, at least as far as the input operation is concerned. Specifically, C++ skips leading whitespace characters. Then it accumulates nonspace characters to form the string. The string ends at the next whitespace character.

So what happens when you mix integers and strings? Write a program that asks for a person’s name (first name only) and age (in years) and then echoes the input to the standard output. Which do you want to ask for first? Print the information after reading it.

Table 5-1 shows some sample inputs for your program. Next to each one, write your prediction for the program’s output. Then run the program, and write the actual output.

Table 5-1. Sample Inputs for Name and Age

Input

Predicted Output

Actual Output

Ray44

44Ray

Ray 44

44 Ray

Ray 44

44 Ray

44-Ray

Ray-44

Think of the standard input as a stream of characters. Regardless of how the user types those characters, the program sees them arrive one by one. (Okay, they arrive in big chunks, by the buffer-load, but that’s a minor implementation detail. As far as you are concerned, your program reads one character at a time, and it doesn’t matter that the character comes from the buffer, not the actual input device.) Thus, the program always maintains the notion of a current position in the stream. The next read operation always starts at that position.

Before starting any input operation, if the character at the input position is a whitespace character, the program skips (that is, reads and discards) that character. It keeps reading and discarding characters until it reaches a nonspace character. Then the actual read begins.

If the program attempts to read an integer, it grabs the character at the input position and checks whether it is valid for an integer. If not, the read fails, and the input position does not move. Otherwise, the input operation keeps the character and all subsequent characters that are valid elements of an integer. The input operation interprets the text as an integer and stores the value in the variable. Thus, after reading an integer, you know that the input position points to a character that is not a valid integer character.

When reading a string, all the characters are grabbed from the stream until a whitespace character is reached. Thus, the string variable does not contain any whitespace characters. The next read operation will skip over the whitespace, as described earlier.

The input stream ends at the end of the file (if reading from a file), when the user closes the console or terminal, or when the user types a special keystroke sequence to tell the operating system to end the input (such as Control+D on UNIX or Control+Z on DOS or Windows). Once the end of the input stream is reached, all subsequent attempts to read from the stream will fail. This is what caused the loop to end in Exploration 2.

Listing 5-3 shows my version of the name-first program. Naturally, your program will differ in the details, but the basic outline should agree with yours.

Listing 5-3.  Getting the User’s Name and Age

#include <iostream>
#include <string>
 
int main()
{
   std::cout << "What is your name? ";
   std::string name{};
   std::cin >> name;
 
   std::cout << "Hello, " << name << ", how old are you? ";
   int age{};
   std::cin >> age;
 
   std::cout << "Good-bye, " << name << ". You are " << age << " year";
   if (age != 1)
      std::cout << 's';
   std::cout << " old. ";
}

Now modify the program to reverse the order of the name and age and try all the input values again. Explain what you observe.

_____________________________________________________________

_____________________________________________________________

_____________________________________________________________

When an input operation fails due to malformed input, the stream enters an error state; e.g., the input stream contains the string “Ray” when the program tries to read an integer. All subsequent attempts to read from the stream result in an error, without actually trying to read. Even if the stream subsequently tries to read a string, which would otherwise succeed, the error state is sticky, and the string read fails too.

In other words, when the program cannot read the user’s age, it won’t be able to read the name either. That’s why the program gets both name and age correct, or it gets both wrong.

Listing 5-4 shows my version of the age-first program.

Listing 5-4.  Getting the User’s Age and Then Name

#include <iostream>
#include <string>
 
int main()
{
   std::cout << "How old are you? ";
   int age{};
   std::cin >> age;
 
   std::cout << "What is your name? ";
   std::string name{};
   std::cin >> name;
 
   std::cout << "Good-bye, " << name << ". You are " << age << " year";
   if (age != 1)
      std::cout << 's';
   std::cout << " old. ";
}

Table 5-2 shows a truncated version of the output (just the name and age) in each situation.

Table 5-2. Interpreting Input the C++ Way

Input

Name First

Age First

Ray44

"Ray44", 0

0, ""

44Ray

"44Ray", 0

44, "Ray"

Ray 44

"Ray", 44

0, ""

44 Ray

"44", 0

44, "Ray"

Ray 44

"Ray", 44

0, ""

44 Ray

"44", 0

44, "Ray"

44#Ray

"44#Ray", 0

44, "#Ray"

Ray#44

"Ray#44", 0

0, ""

Handling errors in an input stream requires some more advanced C++, but handling errors in your code is something you can take care of right now. The next Exploration helps you untangle compiler error messages.

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

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