Using String-based Data Validation

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 library offers only a limited input-validation mechanism. How can you ensure that users don’t accidentally enter a string as their zip code, for example? Although cin can detect such anomalous conditions, it has a cumbersome interface that doesn’t provide sufficient information about the cause of the error. Worse yet, cin doesn’t recover from errors automatically.



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.

What Is Wrong with Datatype Analysis?
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 Better
Instead 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 takes three parameters: two input iterators that mark the sequence’s beginning and end, respectively and a predicate. Our predicate can be an address of a function, say isalpha() or it can be a function object whose overloaded () operator returns bool. For each character in the string num, find_if() calls the predicate. If any of the characters isn’t a digit, find_if() returns an iterator pointing to the first non-digit character. Otherwise, it returns a value equal to the second argument. In other words, the user’s input is kosher if find_if() returns num.end().

The nondigit function object uses the standard function isdigit() declared in . It negates the result of isdigit() and returns it:

class nondigit{public: bool operator() (char c) {return !isdigit(c);}};

If the string is kosher, we can proceed to the next step, namely converting it to int. We use a stringstream object for this:

if(kosher==true){stringstream s(num); //insert string to ss>>passenger; //extract int value and write it to passenger}

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.

Share the Post:
Share on facebook
Share on twitter
Share on linkedin

Overview

Recent Articles: