hat do a command shell, an online ordering system, and a report generator application have in common? Not much, really. Yet they all base their operation on an external source of data, be it a human user or a data file. By their nature, such applications must be prepared to detect invalid input and handle it in a reasonable manner.
The standard
Instead of relying on cin to validate input, read the input as a string and then check it. If the string passes the test, convert it to the desired type, say int or double.
Suppose we want to develop a typical online flight booking system in which the user fills out the number of passengers, the requested date, the destination, the name etc. We need to determine the underlying types of each of the fields that the user should enter. For instance, the number of passengers is int whereas a passenger’s name is of type string:
class booking{private: int passengers; string name;//..};
We then read these fields as follows:
void booking::get_details(){ cout<< "enter number of passengers: "<< endl; cin>>passengers;//}
Alas, cin doesn’t block users from entering patently wrong data. If the user inserts the string “2w” as the number of passengers, cin will treat the letter ‘w’ as the beginning of the next field. Thus, not only do we end up with a dubious number of passengers, but the next field will contain a garbage value. As a partial solution, we can check the state of cin after each input operation by calling fail(). If cin is in an anomalous condition, fail() returns true. To see how it works, call get_details() again and insert the string “zz” as the number of passengers:
enter number of passengers: zz
As expected, a subsequent call to cin.fail() returns true. Yet we can’t tell what exactly is wrong with the input; all we know is that the user has entered an incorrect value (at this point, our passenger field contains a garbage value). To fix this, we can read the input in a loop, asking the users to re-enter this datum if fail() is true. This, however, isn’t an ideal solution for several reasons. To begin with, we don’t know what exactly is wrong with the input, so we can’t give the users a detailed error message. Furthermore, cin doesn’t clear the error automatically; you’d have to call clear() to clear the error. Obviously, we need a better validation method.
String-based Data Validation Works BetterInstead of relying on a field’s datatype, read all the values as strings and test them. A valid number of passengers, for example, should only contain digits. The revised get_details() function now looks as follows:
#include void booking::get_details(){ string num; bool kosher=false; cout<< "enter number of passengers: "<> endl; cin>>num; //make sure that num contains digits exclusively if(find_if(num.begin(),num.end(), nondigit())==num.end()) kosher=true; // we have a valid string else // we have an invalid string}
The interesting part is the find_if() call. The algorithm find_if() defined in The nondigit function object uses the standard function isdigit() declared in If the string is kosher, we can proceed to the next step, namely converting it to int. We use a stringstream object for this: In case of an invalid string, we can display a detailed error message to the user, possibly highlighting the first invalid characters in the entered string.class nondigit{public: bool operator() (char c) {return !isdigit(c);}};
if(kosher==true){stringstream s(num); //insert string to ss>>passenger; //extract int value and write it to passenger}