Coming Soon
This lesson is currently being developed
std::cin and handling invalid input
Learn to handle invalid user input safely.
What to Expect
Comprehensive explanations with practical examples
Interactive coding exercises to practice concepts
Knowledge quiz to test your understanding
Step-by-step guidance for beginners
Development Status
Content is being carefully crafted to provide the best learning experience
Preview
Early Preview Content
This content is still being developed and may change before publication.
9.5 — std::cin and handling invalid input
In this lesson, you'll learn how to handle invalid user input when using std::cin, including input validation, error recovery, and creating robust input routines.
Understanding std::cin behavior
std::cin is the standard input stream in C++, but it can behave unexpectedly when users enter invalid data. Understanding how it works is crucial for writing robust programs that handle bad input gracefully.
How std::cin processes input
#include <iostream>
void demonstrateBasicInput()
{
int number;
std::cout << "Enter an integer: ";
std::cin >> number;
if (std::cin.good())
{
std::cout << "You entered: " << number << std::endl;
}
else
{
std::cout << "Input failed!" << std::endl;
}
}
int main()
{
std::cout << "Basic input demonstration:" << std::endl;
demonstrateBasicInput();
return 0;
}
Sample runs:
Enter an integer: 42
You entered: 42
Enter an integer: abc
Input failed!
When you enter "abc" for an integer, std::cin fails and enters an error state.
Stream states and error checking
std::cin has several states that indicate what happened during input:
#include <iostream>
#include <limits>
void checkStreamStates()
{
int number;
std::cout << "Enter an integer: ";
std::cin >> number;
std::cout << "\nStream states after input:" << std::endl;
std::cout << "good(): " << std::cin.good() << " (no errors)" << std::endl;
std::cout << "eof(): " << std::cin.eof() << " (end of file)" << std::endl;
std::cout << "fail(): " << std::cin.fail() << " (logical error)" << std::endl;
std::cout << "bad(): " << std::cin.bad() << " (read error)" << std::endl;
if (std::cin.fail())
{
std::cout << "\nInput failed - clearing error state..." << std::endl;
std::cin.clear(); // Clear error flags
// Skip the invalid input
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::cout << "Stream cleared. good(): " << std::cin.good() << std::endl;
}
if (std::cin.good())
{
std::cout << "Successfully read: " << number << std::endl;
}
else
{
std::cout << "Could not read a valid integer" << std::endl;
}
}
int main()
{
checkStreamStates();
return 0;
}
Sample run with invalid input:
Enter an integer: hello
Stream states after input:
good(): 0 (no errors)
eof(): 0 (end of file)
fail(): 1 (logical error)
bad(): 0 (read error)
Input failed - clearing error state...
Stream cleared. good(): 1
Could not read a valid integer
Creating robust input functions
Safe integer input
#include <iostream>
#include <limits>
int getValidInteger()
{
int number;
while (true)
{
std::cout << "Enter an integer: ";
std::cin >> number;
if (std::cin.good())
{
// Check if there's extra input after the number
char nextChar;
if (std::cin.get(nextChar) && nextChar != '\n')
{
std::cout << "Error: Extra characters after number. Try again." << std::endl;
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
continue;
}
return number; // Valid integer
}
else
{
std::cout << "Error: Invalid input. Please enter an integer." << std::endl;
// Clear the error state
std::cin.clear();
// Skip the invalid input
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
}
}
int getIntegerInRange(int min, int max)
{
int number;
while (true)
{
number = getValidInteger();
if (number >= min && number <= max)
{
return number;
}
else
{
std::cout << "Error: Number must be between " << min << " and " << max << ". Try again." << std::endl;
}
}
}
int main()
{
std::cout << "Testing safe integer input:" << std::endl;
int num1 = getValidInteger();
std::cout << "You entered: " << num1 << std::endl;
std::cout << "\nTesting integer range input (1-100):" << std::endl;
int num2 = getIntegerInRange(1, 100);
std::cout << "You entered: " << num2 << std::endl;
return 0;
}
Sample run:
Testing safe integer input:
Enter an integer: abc
Error: Invalid input. Please enter an integer.
Enter an integer: 123.45
Error: Extra characters after number. Try again.
Enter an integer: 42
You entered: 42
Testing integer range input (1-100):
Enter an integer: 150
Error: Number must be between 1 and 100. Try again.
Enter an integer: -5
Error: Number must be between 1 and 100. Try again.
Enter an integer: 75
You entered: 75
Safe floating-point input
#include <iostream>
#include <limits>
double getValidDouble()
{
double number;
while (true)
{
std::cout << "Enter a number: ";
std::cin >> number;
if (std::cin.good())
{
// Check for extra input
char nextChar;
if (std::cin.get(nextChar) && nextChar != '\n')
{
std::cout << "Error: Extra characters after number. Try again." << std::endl;
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
continue;
}
// Check for special float values
if (std::isnan(number))
{
std::cout << "Error: NaN is not a valid number. Try again." << std::endl;
continue;
}
if (std::isinf(number))
{
std::cout << "Error: Infinity is not a valid number. Try again." << std::endl;
continue;
}
return number;
}
else
{
std::cout << "Error: Invalid input. Please enter a number." << std::endl;
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
}
}
double getPositiveDouble()
{
double number;
while (true)
{
number = getValidDouble();
if (number > 0)
{
return number;
}
else
{
std::cout << "Error: Number must be positive. Try again." << std::endl;
}
}
}
int main()
{
std::cout << "Testing safe double input:" << std::endl;
double num1 = getValidDouble();
std::cout << "You entered: " << num1 << std::endl;
std::cout << "\nTesting positive double input:" << std::endl;
double num2 = getPositiveDouble();
std::cout << "You entered: " << num2 << std::endl;
return 0;
}
Safe string input
#include <iostream>
#include <string>
#include <algorithm>
std::string getValidString(const std::string& prompt, bool allowEmpty = false)
{
std::string input;
while (true)
{
std::cout << prompt;
std::getline(std::cin, input);
if (!allowEmpty && input.empty())
{
std::cout << "Error: Input cannot be empty. Try again." << std::endl;
continue;
}
return input;
}
}
std::string getStringWithLength(const std::string& prompt, int minLength, int maxLength)
{
std::string input;
while (true)
{
input = getValidString(prompt, minLength == 0);
int length = static_cast<int>(input.length());
if (length >= minLength && length <= maxLength)
{
return input;
}
else
{
std::cout << "Error: Input length must be between " << minLength
<< " and " << maxLength << " characters. (Got " << length << ")" << std::endl;
}
}
}
std::string getAlphabeticString(const std::string& prompt)
{
std::string input;
while (true)
{
input = getValidString(prompt);
bool allAlpha = std::all_of(input.begin(), input.end(),
[](char c) { return std::isalpha(c) || c == ' '; });
if (allAlpha)
{
return input;
}
else
{
std::cout << "Error: Input must contain only letters and spaces. Try again." << std::endl;
}
}
}
int main()
{
// Clear any leftover input from previous cin operations
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::cout << "Testing string input validation:" << std::endl;
std::string name = getAlphabeticString("Enter your name: ");
std::cout << "Hello, " << name << "!" << std::endl;
std::string password = getStringWithLength("Enter password (8-20 chars): ", 8, 20);
std::cout << "Password set successfully!" << std::endl;
return 0;
}
Menu-driven input validation
Creating robust menu systems:
#include <iostream>
#include <limits>
#include <vector>
#include <string>
class Menu
{
private:
std::vector<std::string> options;
std::string title;
public:
Menu(const std::string& menuTitle) : title(menuTitle) {}
void addOption(const std::string& option)
{
options.push_back(option);
}
void display() const
{
std::cout << "\n" << title << std::endl;
std::cout << std::string(title.length(), '=') << std::endl;
for (int i = 0; i < static_cast<int>(options.size()); ++i)
{
std::cout << (i + 1) << ". " << options[i] << std::endl;
}
std::cout << "0. Exit" << std::endl;
}
int getChoice() const
{
int choice;
while (true)
{
display();
std::cout << "\nEnter your choice (0-" << options.size() << "): ";
if (std::cin >> choice)
{
// Check for extra characters
char nextChar;
if (std::cin.get(nextChar) && nextChar != '\n')
{
std::cout << "Error: Please enter only a number." << std::endl;
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
continue;
}
if (choice >= 0 && choice <= static_cast<int>(options.size()))
{
return choice;
}
else
{
std::cout << "Error: Choice must be between 0 and " << options.size() << "." << std::endl;
}
}
else
{
std::cout << "Error: Invalid input. Please enter a number." << std::endl;
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
}
}
};
void calculator()
{
Menu calcMenu("Calculator");
calcMenu.addOption("Addition");
calcMenu.addOption("Subtraction");
calcMenu.addOption("Multiplication");
calcMenu.addOption("Division");
int choice;
do
{
choice = calcMenu.getChoice();
if (choice >= 1 && choice <= 4)
{
double a, b;
std::cout << "Enter first number: ";
std::cin >> a;
while (std::cin.fail())
{
std::cout << "Invalid input. Enter a number: ";
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::cin >> a;
}
std::cout << "Enter second number: ";
std::cin >> b;
while (std::cin.fail())
{
std::cout << "Invalid input. Enter a number: ";
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::cin >> b;
}
double result;
bool valid = true;
switch (choice)
{
case 1:
result = a + b;
std::cout << a << " + " << b << " = " << result << std::endl;
break;
case 2:
result = a - b;
std::cout << a << " - " << b << " = " << result << std::endl;
break;
case 3:
result = a * b;
std::cout << a << " * " << b << " = " << result << std::endl;
break;
case 4:
if (b == 0)
{
std::cout << "Error: Division by zero!" << std::endl;
valid = false;
}
else
{
result = a / b;
std::cout << a << " / " << b << " = " << result << std::endl;
}
break;
}
if (valid)
{
std::cout << "Press Enter to continue...";
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::cin.get();
}
}
} while (choice != 0);
std::cout << "Calculator exited." << std::endl;
}
int main()
{
calculator();
return 0;
}
Advanced input validation techniques
Using string streams for validation
#include <iostream>
#include <sstream>
#include <string>
#include <limits>
bool tryParseInt(const std::string& input, int& result)
{
std::stringstream ss(input);
// Try to read an integer
if (!(ss >> result))
{
return false; // Conversion failed
}
// Check if there's any remaining input
std::string remaining;
if (ss >> remaining)
{
return false; // Extra characters found
}
return true; // Success
}
bool tryParseDouble(const std::string& input, double& result)
{
std::stringstream ss(input);
if (!(ss >> result))
{
return false;
}
// Check for special values
if (std::isnan(result) || std::isinf(result))
{
return false;
}
std::string remaining;
if (ss >> remaining)
{
return false;
}
return true;
}
int getSafeInteger(const std::string& prompt)
{
std::string input;
int result;
while (true)
{
std::cout << prompt;
std::getline(std::cin, input);
if (input.empty())
{
std::cout << "Error: Please enter a value." << std::endl;
continue;
}
if (tryParseInt(input, result))
{
return result;
}
else
{
std::cout << "Error: '" << input << "' is not a valid integer." << std::endl;
}
}
}
double getSafeDouble(const std::string& prompt)
{
std::string input;
double result;
while (true)
{
std::cout << prompt;
std::getline(std::cin, input);
if (input.empty())
{
std::cout << "Error: Please enter a value." << std::endl;
continue;
}
if (tryParseDouble(input, result))
{
return result;
}
else
{
std::cout << "Error: '" << input << "' is not a valid number." << std::endl;
}
}
}
int main()
{
std::cout << "Advanced input validation using string streams:" << std::endl;
int age = getSafeInteger("Enter your age: ");
std::cout << "Age: " << age << std::endl;
double height = getSafeDouble("Enter your height in meters: ");
std::cout << "Height: " << height << " meters" << std::endl;
return 0;
}
Sample run:
Advanced input validation using string streams:
Enter your age: 25abc
Error: '25abc' is not a valid integer.
Enter your age: 25
Age: 25
Enter your height in meters: 1.75
Height: 1.75 meters
Input validation with retry limits
#include <iostream>
#include <limits>
template<typename T>
bool getInputWithRetries(const std::string& prompt, T& result, int maxRetries = 3)
{
for (int attempt = 1; attempt <= maxRetries; ++attempt)
{
std::cout << prompt;
if (attempt > 1)
{
std::cout << " (Attempt " << attempt << "/" << maxRetries << ")";
}
std::cout << ": ";
if (std::cin >> result)
{
// Check for extra input
char nextChar;
if (std::cin.get(nextChar) && nextChar != '\n')
{
std::cout << "Error: Extra characters detected." << std::endl;
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
else
{
return true; // Success
}
}
else
{
std::cout << "Error: Invalid input type." << std::endl;
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
if (attempt < maxRetries)
{
std::cout << "Please try again." << std::endl;
}
}
std::cout << "Maximum attempts exceeded." << std::endl;
return false; // Failed
}
bool getValidatedAge(int& age)
{
while (true)
{
if (!getInputWithRetries("Enter your age (0-150)", age))
{
return false; // User exhausted retries
}
if (age >= 0 && age <= 150)
{
return true; // Valid age
}
std::cout << "Age must be between 0 and 150." << std::endl;
}
}
int main()
{
std::cout << "Input validation with retry limits:" << std::endl;
int age;
if (getValidatedAge(age))
{
std::cout << "Thank you! Your age is " << age << "." << std::endl;
}
else
{
std::cout << "Unable to get valid input. Exiting." << std::endl;
return 1;
}
return 0;
}
Handling different input scenarios
Mixed input types
#include <iostream>
#include <string>
#include <sstream>
struct PersonInfo
{
std::string name;
int age;
double height;
char gender;
};
PersonInfo getPersonInfo()
{
PersonInfo person;
// Clear any leftover input
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
// Get name (can have spaces)
while (true)
{
std::cout << "Enter full name: ";
std::getline(std::cin, person.name);
if (!person.name.empty())
{
break;
}
std::cout << "Name cannot be empty." << std::endl;
}
// Get age (integer)
while (true)
{
std::cout << "Enter age: ";
if (std::cin >> person.age && person.age >= 0 && person.age <= 150)
{
break;
}
std::cout << "Please enter a valid age (0-150)." << std::endl;
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
// Get height (double)
while (true)
{
std::cout << "Enter height in meters: ";
if (std::cin >> person.height && person.height > 0 && person.height < 3.0)
{
break;
}
std::cout << "Please enter a valid height (0-3 meters)." << std::endl;
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
// Get gender (character)
while (true)
{
std::cout << "Enter gender (M/F/O): ";
if (std::cin >> person.gender)
{
person.gender = std::toupper(person.gender);
if (person.gender == 'M' || person.gender == 'F' || person.gender == 'O')
{
break;
}
}
std::cout << "Please enter M, F, or O." << std::endl;
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
return person;
}
void displayPersonInfo(const PersonInfo& person)
{
std::cout << "\nPerson Information:" << std::endl;
std::cout << "Name: " << person.name << std::endl;
std::cout << "Age: " << person.age << std::endl;
std::cout << "Height: " << person.height << " meters" << std::endl;
std::cout << "Gender: " << person.gender << std::endl;
}
int main()
{
std::cout << "Enter person information:" << std::endl;
PersonInfo person = getPersonInfo();
displayPersonInfo(person);
return 0;
}
Best practices for input validation
1. Always validate user input
// Good: Validate all input
int getPositiveInteger()
{
int num;
while (true)
{
std::cout << "Enter a positive integer: ";
if (std::cin >> num && num > 0)
{
return num;
}
std::cout << "Error: Please enter a positive integer." << std::endl;
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
}
// Poor: No validation
int getBadInteger()
{
int num;
std::cout << "Enter a number: ";
std::cin >> num; // What if user enters "abc"?
return num; // Undefined behavior possible
}
2. Provide clear error messages
// Good: Specific error messages
int getGradeScore()
{
int score;
while (true)
{
std::cout << "Enter test score (0-100): ";
if (std::cin >> score)
{
if (score >= 0 && score <= 100)
{
return score;
}
else
{
std::cout << "Error: Score must be between 0 and 100. You entered " << score << "." << std::endl;
}
}
else
{
std::cout << "Error: Please enter a numeric value." << std::endl;
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
}
}
// Poor: Vague error messages
int getBadGradeScore()
{
int score;
std::cout << "Enter score: ";
std::cin >> score;
if (score < 0 || score > 100)
{
std::cout << "Bad input!" << std::endl; // Not helpful
return -1;
}
return score;
}
3. Handle stream cleanup properly
void cleanupExample()
{
int num;
std::cout << "Enter a number: ";
if (std::cin >> num)
{
std::cout << "You entered: " << num << std::endl;
}
else
{
std::cout << "Input failed!" << std::endl;
// Essential cleanup steps:
std::cin.clear(); // Clear error flags
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // Skip bad input
}
}
Summary
Handling invalid input with std::cin is crucial for robust C++ programs:
Key concepts:
- Stream states (good, fail, bad, eof) indicate input status
- Use
std::cin.clear()
to reset error flags - Use
std::cin.ignore()
to skip invalid input - Always validate input ranges and formats
Best practices:
- Validate all user input before using it
- Provide clear, specific error messages
- Clean up stream state after failed input
- Use loops to retry invalid input
- Consider using
std::getline()
with string parsing for complex validation - Set reasonable retry limits to prevent infinite loops
Common techniques:
- Check stream state after input operations
- Validate input ranges after successful parsing
- Use string streams for advanced parsing
- Create reusable input validation functions
Proper input validation makes your programs more user-friendly and prevents crashes from unexpected input.
Quiz
- What happens when you try to read an integer using std::cin but the user enters text?
- What are the four main stream states and what do they indicate?
- Why do you need to call both
clear()
andignore()
after failed input? - What's the advantage of using
std::getline()
instead of the >> operator for input? - How can you check if a user entered extra characters after a valid number?
Practice exercises
Create robust input functions for these scenarios:
- Write a function that safely reads a password with length requirements
- Create a menu system that handles all types of invalid input gracefully
- Build a number guessing game with proper input validation
- Implement a calculator that validates all mathematical operations and operands
Explore More Courses
Discover other available courses while this lesson is being prepared.
Browse CoursesLesson Discussion
Share your thoughts and questions