c++validationinputuser-input

How to validate mutliple inputs of different variable types within a do/while loop?


When validating user input to check if the user has inputted correct values (i.e. letters for the strings, numbers for the integer), the below code can only detect when the strings have been incorrectly inputted by the user, not the integer. This means that as long as the user enters a number for the age integer, the code doesn't validate the input.

Here is my code.

#include <iostream>
#include <limits>
#include <string>
using namespace std;

string firstName, secondName, homeTown;
int age;

int main() {
    do {
        cout << "Please enter your full name, hometown and age (e.g. John Doe London 25).\n\n";
        cin >> firstName >> secondName >> homeTown >> age;
        if (cin.fail()) {
            cout << "\nInvalid input!\n\n";
            cin.clear();
            cin.ignore();
        }
        else {
            break;
        }
    } while (1);
    cout << "\nYour Name is: " << firstName << " " << secondName << "\n" << "Your Hometown is: " << homeTown << "\n" << "Your Age is: " << age << "\n";
    return 0;
}

Here's an example of how it outputs when everything is inputted correctly:

Please enter your full name, hometown and age (e.g. John Doe London 25).

John Doe London 25

Your Name is: John Doe
Your Hometown is: London
Your Age is: 25

Here's an example how it outputs when only the integer is inputted incorrectly:

Please enter your full name, hometown and age (e.g. John Doe London 25).

1 1 1 a

Invalid input!

Please enter your full name, hometown and age (e.g. John Doe London 25).

Here's the main issue, the output I get no matter what as long as the integer is inputted correctly, no matter how the strings are inputted:

Please enter your full name, hometown and age (e.g. John Doe London 25).

1 1 1 1

Your Name is: 1 1
Your Hometown is: 1
Your Age is: 1

To summarise, how exactly do I go about validating both an integer and multiple strings with an if statement?


Solution

  • How exactly do I go about validating both an integer and multiple strings with an if statement?

    When you need to validate the entries made by a user, you are usually better off inputting them one at a time. That way, you can check each one as it is entered, and ask the user to try again, when one of them is invalid.

    I generally call a dedicated function for each input I want the user to enter. Function get_int, for instance, displays a prompt, and keeps looping until the user has entered a valid number. Entries must lie be between min and max, or else the user has to try again!

    int get_int(
        std::string const& prompt,
        int const min,
        int const max)
    {
        for (;;) {
            std::cout << prompt;
            if (int n{}; !(std::cin >> n)) {
                std::cout << "Invalid entry. Please reenter.\n\n";
                std::cin.clear();
                std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
            }
            else if (n < min || n > max) {
                std::cout << "Invalid entry. Please reenter.\n"
                    << "Entries must be between " << min << " and " << max << ".\n\n";
            }
            else {
                // The call to `ignore` ensures that this function "plays nice" 
                // with `std::getline`.
                std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
                std::cout << '\n';
                return n;
            }
        }
    }
    

    Here is a very similar function that inputs strings. It uses std::getline, which allows string entries to contain spaces. That way, you do not have to input first and last names separately (unless you want to). Entries are not allowed to be blank.

    std::string get_non_empty_string(std::string const& prompt)
    {
        for (;;) {
            std::cout << prompt;
            if (std::string s; !(std::getline(std::cin, s))) {
                throw std::runtime_error{ "get_non_empty_string: `std::getline` failed" };
            }
            else if (s.empty()) {
                std::cout << "Invalid entry. Please reenter.\n"
                    << "Entries cannot be blank.\n\n";
            }
            else {
                std::cout << '\n';
                return s;
            }
        }
    }
    

    A complete program

    Using the two functions above allows you to simplify function main. Note that I have dispensed with using namespace std;. Most professionals stay away from it. I have also eliminated the global variables. They may be okay in a small program such as this one, but they can be nasty in larger programs, where mistakes can be difficult to track down.

    // main.cpp
    #include <iostream>
    #include <limits>
    #include <stdexcept>
    #include <string>
    
    //==================================================================
    // get_int
    //==================================================================
    int get_int(
        std::string const& prompt,
        int const min,
        int const max)
    {
        for (;;) {
            std::cout << prompt;
            if (int n{}; !(std::cin >> n)) {
                std::cout << "Invalid entry. Please reenter.\n\n";
                std::cin.clear();
                std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
            }
            else if (n < min || n > max) {
                std::cout << "Invalid entry. Please reenter.\n"
                    << "Entries must be between " << min << " and " << max << ".\n\n";
            }
            else {
                // The call to `ignore` ensures that this function "plays nice" 
                // with `std::getline`.
                std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
                std::cout << '\n';
                return n;
            }
        }
    }
    //==================================================================
    // get_non_empty_string
    //==================================================================
    std::string get_non_empty_string(std::string const& prompt)
    {
        for (;;) {
            std::cout << prompt;
            if (std::string s; !(std::getline(std::cin, s))) {
                throw std::runtime_error{ "get_non_empty_string: `std::getline` failed" };
            }
            else if (s.empty()) {
                std::cout << "Invalid entry. Please reenter.\n"
                    << "Entries cannot be blank.\n\n";
            }
            else {
                std::cout << '\n';
                return s;
            }
        }
    }
    //==================================================================
    // main
    //==================================================================
    int main() {
        std::cout << "Please tell us about yourself.\n\n";
        std::string full_name = get_non_empty_string("Full name: ");
        std::string hometown = get_non_empty_string("Hometown: ");
        int age = get_int("Age: ", 0, 125);
        std::cout 
            << "\nYour Name is: " << full_name<< "\n" 
            << "Your Hometown is: " << hometown << "\n" 
            << "Your Age is: " << age << "\n";
        return 0;
    }
    // end file: main.cpp
    

    Sample output

    No errors were made during the first run.

    Please tell us about yourself.
    
    Full name: Joe Dokes
    
    Hometown: London
    
    Age: 25
    
    
    Your Name is: Joe Dokes
    Your Hometown is: London
    Your Age is: 25
    

    The second time around, mistakes abounded. This run shows the problem with inputting full name, rather than separate first and last names. There was no way to detect that the last name was omitted.

    Please tell us about yourself.
    
    Full name:
    Invalid entry. Please reenter.
    Entries cannot be blank.
    
    Full name: Joe
    
    Hometown:
    Invalid entry. Please reenter.
    Entries cannot be blank.
    
    Hometown: London, England
    
    Age: xxx
    Invalid entry. Please reenter.
    
    Age: 1000
    Invalid entry. Please reenter.
    Entries must be between 0 and 125.
    
    Age: 25
    
    
    Your Name is: Joe
    Your Hometown is: London, England
    Your Age is: 25