0
votes

I'm trying to check if the user has entered their name correctly, with or without space i.e. Joe Bloggs. They cannot have special characters or numbers in their name or it will pop up with an error message i.e. Jo3_Bl0ggs. What I'm trying to do is if they enter their name in the wrong format, an error message will be alerted and the program will ask the user to enter their name again, until they enter it correctly.

I'm using a while loop to do this so if it's correct, I change the value of the flag and break out of the loop, if not I'll rerun the setName() function which asks for their name.

However the problem I'm having is that if they enter for the first time and it's incorrect, it asks them to enter their name again and if the second input is correct a message will say "Welcome Joe Bloggs", but the loop will continue and ask them to enter their name in again.

The only way I can avoid this problem is if the first input is correct, but that kind of defeats the whole point of the try and catch block.

Below is the two functions I'm concerned with. If someone can point me in the right direction, then that would be great. I'm new to c++ which is why I'm a bit confused about this.

inputclass::inputclass(){//empty constructor}

void inputclass::validateName(string name){
    int flag = 1;
    while ( flag == 1){
        try{

            for (int i=0;  i < name.length(); i++){

                if (name[i] != ' '){

                    if (!isalpha(name[i])){

                        cout << "You have entered incorrectly. Please try again. "  << endl;
                        cin.clear();   //I'm trying to clear input buffer but still doesn't work
                        cin.ignore(256, '\n');
                        setName();
                        throw 0;  //do i need this?

                        break; //do i need breaks?

                    }else if(i == name.length()-1){

                        cout << "Welcome to the program " << name << endl;
                        flag = 2;   //break out of while loop
                        break;  //not sure if this does anything
                    }
                }
            }
        }catch (int e){
            cout << "There's been an error" << endl;
            setName();   //until input is correct, this will ask the user for their name.
        }
    }
}
void inputclass::setName(){

    string name;
    cout << "Please enter your name: " << endl;
    getline(cin, name);
    validateName(name);


}
1
don't use exceptions for implementing the logic; exceptions are for handling errors, not for fancy if-clauses.Pavel
Shall I just take out the try and catch and stick to if/elses?jellybean_232
that's what I'd suggest. you can still ask if (flag != 2) after the while loop in order to check for errors.Pavel
If flag can only be 1 or 2 consider changing it to a bool. You can also get rid of the break statements since you have no logic after the if/else blocksCaptain Obvlious
i've taken the try and catch out and changed the flag to booleans, I still think the input buffer isn't clearing so even if the next input is correct, it's still asking for the name again.jellybean_232

1 Answers

2
votes

My honest opinion is that your algorithm logic itself is wrong. Validation should be done as a consequence of input. The input itself should be the decision point of recycling on validation failure

1. Write a simple validation function

This is easier than you may think, in particular since the standard library provides some neat algorithms for doing much of the iteration work for you.

2. Integrate your validation function exclusively in setName(); not the opposite.

Your input mechanics should take said-input, validate, and if invalid, loop on the input. The validator should exclusively be returning yea or nay. Leave the input mechanics to the input function, and don't trigger them from the validator. A validator is for just that; validating, not cycling input loops.


The first of these is fairly simple since the standard library provides you with most of the algorithms you need. The sample below uses a functor to do this, but C++11 could easily do this with a lambda instead:

struct is_not_space_or_alpha
{
    bool operator()(char c) const
    {
        return c != ' ' && !std::isalpha(static_cast<unsigned char>(c));
    }
};

static bool is_valid_name(const std::string& name)
{
    return !name.empty() &&
        name.end() != std::find_if(name.begin(), name.end(), is_not_space_or_alpha());
}

With that, your setName() becomes the location where validation is a consequence of input; the validation doesn't internally trigger more input. That is left to the caller (setName()) to decide:

std::string name;
while (true)
{
    std::cout << "Please enter your name: ";
    std::flush(std::cout);

    if (std::getline(std::cin, name) && is_valid_name(name))
    {
        std::cout << "Welcome to the program " << name << "!\n";
        break;
    }

    std::cout << "You have entered incorrectly. Please try again. \n";
    std::cin.clear();
}

Sample Output

Please enter your name: Monkey777
You have entered incorrectly. Please try again. 
Please enter your name: Wiley Coyote
Welcome to the program Wiley Coyote!

The Bottom Line: Don't trigger input from validation. Instead, trigger validation from input and act accordingly.