Search code examples
c++hexscanfuser-inputiostream

Reliably get user hex input in C++ with error handling


I've looked around on the internet for a while now and I've found that the C++ way of getting user input as a hexadecimal number is using cin >> hex >> variable. I can't currently get this to work in my situation. Here's what I want to happen for a range of user inputs:

>0xFF    -> variable = 0xFF;
>FF      -> variable = 0xFF;
>122     -> variable = 0x122;
>garbage -> ask again;
>0XFfFfA -> variable = 0xFFFFA;

Here is the code I have and the problem with it:

int endAddr;
do { printf("Enter end addr: ");   } while (!(cin >> hex >> endAddr));

Problem: after entering garbage, it continues to loop printing out "Enter end addr: " instead of letting the user try again.

Any help regarding this code snippet would be very useful. Thanks in advance.

EDIT:

As per suggestion I've looked at this post and adjusted my code accordingly:

while (true) {
    printf("Enter start addr: ");
    if (cin >> hex >> startAddr) {
        break;
    }
    cin.clear();
    cin.ignore();
}
//rest of code

The issue now is that if I enter a string such as le, it prints the prompt again however it then continues to the rest of the code. Why is this? If I remove cin.ignore(), it does the loopy thing discribed above.


Solution

  • cin.ignore() will ignore the last character in its internal buffer. You need to use cin.ignore(std::numeric_limits<std::streamsize>::max()) to reset back to the last EOF. Then you can check the next input reliably.

    After trying to work this one out (good practice with streams :D), I have come up with a solution

        int startAddr;
        std::cin.exceptions(std::ios_base::failbit);
    
        while (true) {
            std::cout << "Enter start addr: ";
    
            try {
                std::cin >> std::hex >> startAddr;
                break;
            } catch(std::exception&) {
                std::cin.clear();
                std::cin.ignore(std::numeric_limits<std::streamsize>::max(),'\n');
                std::cout << "Invalid number\n"; 
            }
        }
    

    This makes use of the ability to set a failbit as an exception for std::iostreams meaning we are guaranteed to catch the error and std::numeric_limits as a more reliable way to clear the buffer