Reading Data Pattern: Raw and Toolkit Based Input

Pattern Name:

Reading Data Pattern: Raw and Toolkit Based Input

Author:

Viera K. Proulx

Intent and Motivation:

Many computer programs require that the user types in some data. The data is then stored in memory locations specified by variable names or object names. The user is allowed to correct mistakes, before hitting the return, thus indicating the end of submission. The program then reads all data typed in the one line and tries to interpret the keystroke sequence as a value of a variable of the specified type.

There are several issues that have to be addressed when designing the input processing. Do we expect the user to supply only valid data? This may be possible if the input comes from a computer-generated file. However, input from a keyboard may contain unintentional errors and the program may need to deal with this possibility. Sometimes the program may anticipate user's response. For example, when asking the user whether to continue with the next case, the anticipated answer would be YES. In such cases, the input-processing program may supply a default value that the user can accept or override. Finally, we may allow for the possibility that no input will be given. For example, no input may signal an end of a series of data entries.

Typical programming languages support only raw input - input statements or functions that assume that the input will be supplied without errors. Commercial level programmers will always use error checking before the input is accepted.

Problem Examples:

  1. A program reads user's name and year of graduation and prints a name tag for a reception.
  2. A program reads a series of names of cities, the high and low temperature for the day and creates a map where the temperature is represented by a color code.

Problem and Context:

We will look at several methods for processing user input. In the simplest case, we expect the user to be infallible and provide the program with correct input every time. The built-in input functions/statements in most programming languages support this mode of input. We call this the raw input.

In industry, the programmer must assume that the user may make a mistake and the program needs to detect and signal such errors. Any large scale project will build a set of tools to be used to filter the user input and signal errors. So, when the user types in the input, the input data stream (the sequence of characters typed in) is checked for correctness. If the user provided correct values, input is accepted and the program continues. If the input checker detects an error (including no input at all, just an empty line), this fact is signaled to the user (through an error message) and the user is asked to retype the input. We call this a verified input.

There are two basic variations of this pattern. First, there may be a preferred value of user input - the default that the user will type most of the time. In that case, the input processing function may notify the user that it knows a possible default value. If the user does not type in any data, the default values are used. However, errors in input are signaled back to the user followed with a prompt for resubmission. In this mode the user must supply a value and the program refuses to continue until a correct input is provided. We call this verified input with default.

Another variation is used when we expect the user to submit a series of data items. The end of line signals only the end of one data item. We need to decide how the user will signal the end of the data series. There are two possible solutions. The programmer may select a data value that will not appear in the input sequence (this is called a sentinel) and the user indicates the end of the data series by typing in the sentinel value. A better solution is to interpret an empty input line (user only hits the return key) as a signal that ends the series of data item entries. We call this a verified conditional input.

Sometimes user wants to enter several data items on one line. Similarly, a file may be formatted, so that specific positions correspond to specific data items in one record (name, age, date of birth, etc). This type of input requires a different approach and will be discussed in the Filtered Input Data Pattern.

We will explain the four simple input modes in the context of the IOTools toolkit.

Required Elements and Structure:

Raw Input

The basic C++ statement that read the input stream from the keyboard (the sequence of keystrokes typed in followed by the return key) is the following:

cin >> x;

If we type in more than one number, separated by spaces or commas, only the first number will be used to determine the value of the variable x. If we type in a number with a decimal point, it will be converted to an integer value by forgetting all digits after the decimal point. However, if we type in some letters or other keystrokes that the computer cannot interpret as an integer value, our program crashes - it stops, because the computer has no instructions for how to recover from this error. (The exact nature of the failure depends on the compiler.)

When typing in strings (sequences of letters), we are faced with an additional problem. Suppose we try to read in the name as follows:

cin >> myName;

If the user types in Peter Pan, the computer does not know whether the space between Peter and Pan meant that we want to start a new string Pan or whether the whole name should be used as one string.

Note:

Most textbooks allow students to program quite complex programs using this type of input. As a result, the user has to be extremely careful when running a program, not to crash the program with one careless keystroke. In industry, people need to write robust code - programs that realize user's mistakes and allow the user to enter the input again. To make this possible in your assignments, we will be using IOTools toolkit that provides such safety features for user input.

Verified Input

To ask the user to type in an integer and to assign the user's answer to be the new value of variable x we use the following statement:

x = RequestInt("Next number:");

If the input cannot be interpreted as an integer value, the computer will print an error message and repeat the request for next number - and repeat this until a valid value is supplied.

 

Similarly, the statement:

RequestString("Type in your name", myName);

myName.RequestString("Type in your name");

can be used to request the name to be typed in and to assign the resulting sequence of letters to the string object myName.

RequestString reads the whole line and assigns the entire sequence of characters that was typed in to be the value of the string myName.

Verified Input with Default

The only difference from the verified input is that the program supplies a default value. The default value is displayed after the prompt, to alert the user to its availability and to inform the user of the default value. The statements have the format:

x = RequestInt("Next number:", 3);

RequestString("Type in your town", myTown, "Boston");

myTown.RequestString("Type in your town", "Boston");

Verified Conditional Input

In both previous cases, the user is required to supply input and the program will not proceed further until satisfactory data has been typed in. Sometimes, it is desirable to let the user decide whether or not she will supply the next data item. One example of this is if a user is typing in a series of data, but the program does not know ahead of the time how many items there will be in the series. For example, if the program is computing a price of grocery store purchase, it does not know how many items the buyer will have. The program keeps asking for the next item and its price, but when it does not receive a new entry, it knows that it can continue with printing the totals.

The function that supports verified conditional input returns a boolean value true, if the input was supplied, and returns false when no input was given.

The function is best illustrated as follows:

int total = 0; int item;

while (ReadingInt("Next Bar Code"), item){

total = total + item * Price(item);

}

cout << "Total is: " << total << endl;

Implementation Examples:

The four examples below show four variations for reading five integers and printing their squares.

 

Raw Read: (fails with non-numeric input)

int n;

int i;

for (i = 1; i < 5; i++){

cout << "Type in the next number:";

cin >> n;

cout << n << " : " << n * n << endl;

}

cout << "Done" << endl;

 

Verified Read: (5 numbers must be entered)

int n;

int i;

for (i = 1; i < 5; i++){

n = RequestInt("Type in the next number:");

cout << n << " : " << n * n << endl;

}

cout << "Done" << endl;

 

Verified Read with Default: (5 numbers are expected; ordinal number is default)

int n;

int i;

for (i = 1; i < 5; i++){

n = RequestInt("Type in the next number:", i);

cout << n << " : " << n * n << endl;

}

cout << "Done" << endl;

 

Conditional Verified Read: (counts the number of inputs, prints it at the end)

int n;

int i = 0;

while (ReadingInt("Type in the next number:", n);

i++;

cout << n << " : " << n * n << endl;

}

cout << "Done " << i << " numbers entered." << endl;

Forces and Variations:

You should never use the Raw Read method. Verified Read is used when the program unconditionally requires user input. If you can anticipate user input, Verified Read with Default allows you to notify the user of the default option. When you do not know how many times will the input be supplied, use Conditional Verified Read, giving the user the option to decline further input.

Later we will look at how the input can be further "filtered" or verified to satisfy user-defined conditions.

Summary:

There are four basic methods for requesting one or more data values from the user.

Raw Read provides no error checking. Two statements are needed - an output statement that prints the prompt to the user and a basic input statement. It fails if user makes mistake in typing the input and so it should not be used.

Sometimes this input mode is used to input several data items on one line, where the data items can be of different types - however, the lack of any error checking still remains a problem.

Verified Read is uses a function that prints the given prompt, reads the input, and verifies that the input is of correct type. It repeats the request for input until valid input is supplied. Of course, typing errors that supply the correct data type but with wrong value cannot be detected here.

Verified Read with Default uses a function that prints the given prompt as well as the given default value and awaits input. If the input is not correct, the function repeats the request and gives the user another chance to type in the data. If the user enters no data, the default value is used instead.

Conditional Verified Read, is used when we expect that user may not supply any input at all. It is typically used for a series of data requests. Program then stops asking for further input when user has no more data to process and declines to type in another value.

Remarks:

We do not show here how the IOTools functions would be implemented. The Filtered Input Pattern addresses these issues.