Those Pesky Failures

We noted earlier that sometimes cin will fail when it tries to read a number and the buffer contains non-digits (or non-legal characters -- a floating point value can have a '.', etc.). We learned to use cin's fail function to detect when this happened, but we could do little to rectify the situation unless the error was a single character long. If the user had typed multiple illegal characters, we couldn't fix the situation: until now!

By combining loops with the fail detection we can finally handle multi-character foul-ups. Instead of the fail branch we had earlier (and didn't particularly like):

    cin >> num;    // read numeric var
    if (cin.fail())    // translation failed
    {
        cin.clear();     // clear memory of failure
        cin.ignore();    // throw out (single) offending character
        cin >> num;   // re-read numeric var
    }

We can use a fail loop to clear up any length of error the user might make:

    cin >> num;    // read numeric var
    while (cin.fail())    // translation failed
    {
        cin.clear();     // clear memory of failure
        cin.ignore();    // throw out (single) offending character
        cin >> num;   // re-read numeric var
    }  // loop back to get further characters!

Much better! Now we can fix problems like:

    How much did your apples cost?  $4
    How much did your pears cost?  $5

If we weren't anticipating the '$', it would corrupt the translation and cause problems. With the fail-loop form, however, we can simply clear/ignore that '$' character and move forward to the 4 (and then again for the 5). (And it also allows for more than a single character to be present -- hence the loop.)

We can also fix oddities like:

    How many apples do you have?  4 apples
    How many pears do you have?  5 pears

The read for pears will hit an error when trying to read through the text "apples". With the fail-loop in place, these 6 errors will simply be cleared and thrown out until it finally is allowed to reach the 5 on the next input line.

Preventative Maintenance

That last example, though, wasn't a problem with the input we were doing but with the previous input. It might be nice if the apples input had cleaned up that extra stuff before the pears tried to read.

To solve this sort of problem, we can do something like this after an input:

    while (cin.peek() != '\n')   // aren't about to read '\n'
    {
        cin.ignore();            // throw out extra char
    }

(I.E. after we read the 4 for apples, do this loop to clear out the "apples").

Here we conjoin cin's peek and ignore functions in a loop to throw out all characters until the new-line is found. The new-line itself is left in the input because it won't really cause any problems -- cin skips all whitespace on extraction (>>).

If, though, you are worried about the new-line remaining or you have a situation where you'd like to remove a sequence of characters up to and including the termination character, you can alter the loop to this form:

    do
    {
        ch = cin.get();      // what's next?
    } while (ch != '\n');    // stop when it's a new-line

Here we are not trying to preserve anything in the input stream and so we use the destructive get instead of the non-destructive peek to do our look-ahead.

Whenever the termination character is a '\n', we call these loop forms clear-eol-loops (end-of-line-loops). In more general terms we can call it a clear-thru-loop (do/get form) or a clear-until-loop (while/peek form).

(Of course, we could just use the alternate form of cin.ignore():

    cin.ignore(numeric_limits<streamsize>::max(), '\n');   // numeric_limits<streamsize>::max() is in limits

to clear the entire rest of the line. This is the same effect as the do/get form of the clear-eol-loop...just more compact.)

Combining Fail-Avoidance and Validation

Often we have to validate values and don't want to give up our failure-avoidance protection. How can we combine them? Most likely, we'll use a helper function as we did with multi-validation. But here, the crucial piece is the failure-avoidance and the validation is secondary. So:

    void num_only(______ & n)   // fill in blank with data type you need
    {
        cin >> n;
        while (cin.fail())
        {
            cin.clear();
            cin.ignore();  // hopefully just one offender!
            cin >> n;
        }
        return;
    }

    // ...later...
    ______ data;          // again, fill in the blank with your data type
    cout << "Prompt:  ";
    num_only(data);
    while (data < 0)   // assuming negatives are bad...
    {
        cout << "Error!\n\nPrompt:  ";
        num_only(data);
    }

I expect you can come up with nice prompts and error messages yourself. *smile*


Last modified 09/06/2018 15:28:52.

It is now 3/15/2025, 4:34:31 AM.

Date you last viewed this page: That's for you to know and ...well, that's for you to know.

© 1993-2025  Jason James   (email — craie@acm.org — for permissions &/or details)