0

I know this has been posted a million times because it's like a day 1 problem for any programmer learning C++, but I haven't seen any answers I could understand. Here's what I'm trying to do:

  1. I want to prompt the user to enter a number using std::cin (this part works)
  2. It should give an error and retry if someone enters text like "asdf" (this part works)
  3. It should give an error and retry if the number is less than or equal to 0 (this part works)
  4. It should give an error and retry if someone enters a number with text like "45.7asdf" (this saves as 45.7 with no error)

Here is the code:

    void ClearCin()
    {
        // a standard way of clearing errors caused by invalid user input
        std::cin.clear();
        std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    }    

    float startBalance;
    std::cout << "Please enter the starting balance: ";
    std::cin >> startBalance;
    
    // ask for input until a valid float > 0 is entered
    while ((std::cin.fail()) or (startBalance <= 0))
    {
        ClearCin(); //this is a combination of std::cin.clear() and std::cin.ignore()
        std::cout << "Error: starting balance must be a positive number.\n";
        std::cout << "Starting balance is currently set to " << startBalance << "\n";
        std::cout << "Please enter the starting balance: ";
        std::cin >> startBalance;
    }
    std::cout << "Starting balance has been set to " << startBalance << "\n";

How do I make C++ recognize "45asdf" or "43.1asdf45.1" as garbage answers? In the above code, it will recognize "45asdf" as "45", which is valid. It sees "43.1asdf45.1" as "43.1" which is also valid. It seems to grab as many numbers as it can, as long as those numbers come before letters, and then it discards the rest without any errors. Am I missing something trivial or is this a much more complicated problem than I think it is?

Shawn Darichuk
  • 380
  • 4
  • 9
  • Possible duplicate https://stackoverflow.com/questions/46260058/program-not-outputting-the-correct-standard-deviation-also-problems-with-valid – Galik Jan 17 '22 at 04:22
  • Great find, and that would solve what I'm trying to do. Is it possible to do the same thing using std::cin and not std::istringstream ? – Shawn Darichuk Jan 17 '22 at 04:30
  • 1
    The problem with `std::cin` is you can't test for "end of file" for each number because "end of file" will only occur after all the numbers have been read. That's why I read from `std::cin` into a `std::string` and then put that in a `std::stringstream` for testing. – Galik Jan 17 '22 at 04:37
  • 1
    You don't have to use `std::getline` though, I think there is another example. I'll try to find it. – Galik Jan 17 '22 at 04:38
  • Is this more useful? https://stackoverflow.com/questions/24504582/how-to-test-whether-stringstream-operator-has-parsed-a-bad-type-and-skip-it/27004240#27004240 – Galik Jan 17 '22 at 04:39
  • Awesome. I will try playing around with strings to get this to work. Thanks! – Shawn Darichuk Jan 17 '22 at 04:42

1 Answers1

1

Always get user input as a string, and then try to convert it to your desired datatype. Complain if it fails. After converting you can also test for bounds errors.

Remember, the user will always press Enter after every input you ask for.

#include <ciso646>
#include <iostream>
#include <optional>
#include <sstream>
#include <string>

template <typename T>
auto string_to( const std::string & s )
{
  T value;
  std::istringstream ss( s );
  return ((ss >> value) and (ss >> std::ws).eof())
    ? value
    : std::optional<T> { };
}

int main()
{
  // Ask user for input
  std::cout << "What is your age? ";

  // Get the user’s response
  std::string s;
  getline( std::cin, s );

  // Attempt to convert user’s response to a valid value
  auto age = string_to <int> ( s );
  if (!age or (*age < 0)) // ...and validate it
  {
    std::cerr << "That is not a valid age.\n";
    return 1;
  }

  // Success! Use the answer!
  std::cout << "Good job! You are " << *age << " years old!\n";
}

It is often helpful to make a function to handle specific user inputs, like:

int ask_for_integer( const std::string & prompt, int min, int max )

If you are certain that the user is a human (use isatty()) then you can loop until he/she enters a valid input. Otherwise just rage quit and make them run the program again.

Sorry, fixed typos

Dúthomhas
  • 8,200
  • 2
  • 17
  • 39