Why do I get an infinite loop when I press a letter? How do I change for error checking?
Because std::cin is type safe, it knows that a letter is not a valid input for "int number". It raises an error flag in std::cin and any subsequent operation will fail and return immediately.
You'll need to check the error state and clear any error flag(s) before you can proceed.
See existing post Why do I get an infinite loop if I enter a letter rather than a number?
infinite loop in C when the user enters a character instead of a number
scanf leaves the '\n' char in the input stream so you need to skip it:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
int main()
{
float a;
do
{
printf("Insert number between 1.00 and 10.00:\n");
scanf(" %f", &a);
if (a < 1.00 || a > 10.0)
{
printf("Insert a correct number.\n");
}
} while (a < 1.00 || a > 10.00);
printf("\nyou have entered: %f\n", a);
return 0;
}
https://godbolt.org/z/baPxdn .But it does not sort out the wrong input problem.
I personally prefer to read the line and then scan it (and it does sort all the problems out):
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
int main()
{
char str[100];
float a;
do
{
printf("Insert number between 1.00 and 10.00:\n");
fgets(str, 100, stdin);
if(sscanf(str, " %f", &a) != 1) continue;
if (a < 1.00 || a > 10.0)
{
printf("Insert a correct number.\n");
}
} while (a < 1.00 || a > 10.00);
printf("\nyou have entered: %f\n", a);
return 0;
}
https://godbolt.org/z/vxY7To
Infinite loop with cin when typing string while a number is expected
Well you always will have an infinite loop, but I know what you really want to know is why cin doesn't keep prompting for input on each loop iteration causing your infinite loop to freerun.
The reason is because cin fails in the situation you describe and won't read any more input into those variables. By giving cin bad input, cin gets in the fail state and stops prompting the command line for input causing the loop to free run.
For simple validation, you can try to use cin to validate your inputs by checking whether cin is in the fail state. When fail occurs clear the fail state and force the stream to throw away the bad input. This returns cin to normal operation so you can prompt for more input.
if (cin.fail())
{
cout << "ERROR -- You did not enter an integer";
// get rid of failure state
cin.clear();
// From Eric's answer (thanks Eric)
// discard 'bad' character(s)
cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
For more sophisticated validation, you may wish to read into a string first and do more sophisticated checks on the string to make sure it matches what you expect.
C++: Infinite loops from input validation
What's happening in your code is that whenever you input a letter, cin
keeps its invalid state for subsequent inputs, so you have to clear it and remove bad inputs first. So you can do something like this :
if (!(cin >> n)) {
cout << "THE NUMBER INPUT IS NOT RECOGNISED AS AN INTEGER, TRY AGAIN." << endl;
cin.clear();
while(cin.get() != '\n');
n = 1;
}
Avoiding infinite loop when a char is enter in place of int
Here is another approach that might help; first writing to std::string
and then going over all elements in the string checking if they're digit. Using header <cctype> for isdigit()
and <cstdlib> for std::atoi
, although in c++11 you can use std::stoi
if your compiler supports it.
If you write: 141.4123, the result will be 141 after converting (if you let the user type '.'), the result will be truncated because you convert to an int.
Working example:
int str_check(string& holder, int& x)
{
bool all_digits = true; // we expect that all be digits.
if (cin >> holder) {
for(const auto& i : holder) {
if (!isdigit(i) && i != '.') { // '.' will also pass the test.
all_digits = false;
break;
}
}
if (all_digits) {
x = atoi(holder.c_str()); // convert str to int using std::atoi
return 1;
}
else
return 0;
}
}
int main()
{
int x{};
string holder{};
while (1)
{
if (str_check(holder, x))
cout << x << '\n';
}
return 0;
}
C infinite loop when char input instead of int
Any time you are taking user-input, you must account for each character that remains in the input buffer (stdin
here). This is especially true when taking input with scanf
(or family) due to the way scanf
handles input or matching failures. When either occurs, no further characters are read, and any offending characters are left in the input buffer unread -- just waiting to bite you again on your next attempted read (generally resulting in an infinite loop if you are taking input within a loop)
Further, you must understand how each conversion specier handles leading whitespace and whether leading whitespace is consumed by the specifier (e.g. numeric conversion specifiers and "%s"
) and which do not (all others, particularly "%c"
and "%[...]"
).
This is one of the primary reasons a line-oriented function such as fgets
(with an adequately sized buffer) or POSIX getline
are recommended for taking user input. Both read and include the trailing '\n'
in the buffer filled. This consumes the line completely, eliminating any uncertainty which additional characters remain in the input buffer unread. You can then pass the buffer to sscanf
for parsing. This allows independent validation of both (1) the read ("did I get input?") and (2) the parse of information from the line ("did it contain the information I need?").
scanf
can be used, if used correctly. This means you are responsible for checking the return of scanf
every time. You must handle three conditions
(return == EOF)
the user canceled input by generating a manualEOF
by pressing Ctrl+d (or on windows Ctrl+z, but see CTRL+Z does not generate EOF in Windows 10 (early versions));(return < expected No. of conversions)
a matching or input failure occurred. For a matching failure you must account for every character that will be left in your input buffer. (scan forward in the input buffer reading and discarding characters until a'\n'
orEOF
is found); and finally(return == expected No. of conversions)
indicating a successful read -- it is then up to you to check whether the input meets any additional criteria (e.g. positive integer, positive floating-point, within a needed range, etc..).
Utilizing that with your code, you could take input in the following way -- looping continually until valid input is received or the user cancels by generating a manual EOF
, e.g.
#include <stdio.h>
void empty_stdin (void) /* simple helper-function to empty stdin */
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
int main(void)
{
int input = 0,
rtn = 0; /* variable to save scanf return */
// domainEntry *myDomains = buildDomainDB();
for (;;) { /* loop continually until valid input or EOF */
printf ("\nSelect top level domain:\n"
" 1-EDU\n"
" 2-COM\n"
" 3-ORG\n"
" 4-GOV\n"
" 5-MIL\n"
" 6-CN\n"
" 7-COM.CN\n"
" 8.CAN\n\n"
"choice: ");
rtn = scanf (" %d", &input); /* save return */
if (rtn == EOF) { /* user generates manual EOF */
fputs ("(user canceled input.)\n", stderr);
return 1;
}
else if (rtn == 0) { /* matching failure */
fputs (" error: invalid integer input.\n", stderr);
empty_stdin();
}
else if (input < 1 || 8 < input) { /* validate range */
fputs (" error: integer out of range [1-8]\n", stderr);
empty_stdin();
}
else { /* good input */
empty_stdin();
break;
}
}
printf ("\nvalid input: %d\n", input);
}
(note the use of the helper-function empty_stdin()
-- and that even after a successful read, you should still empty_stdin()
to insure the input buffer is empty and prepared for the next user input -- whatever that may be)
Example Use/Output
$ ./bin/getintmenu
Select top level domain:
1-EDU
2-COM
3-ORG
4-GOV
5-MIL
6-CN
7-COM.CN
8.CAN
choice: edu
error: invalid integer input.
Select top level domain:
1-EDU
2-COM
3-ORG
4-GOV
5-MIL
6-CN
7-COM.CN
8.CAN
choice: 9
error: integer out of range [1-8]
Select top level domain:
1-EDU
2-COM
3-ORG
4-GOV
5-MIL
6-CN
7-COM.CN
8.CAN
choice: 4
valid input: 4
If you do your job, you can successfully use scanf
as needed.
Or, you can make your life much easier and use fgets
and then test whether the first character in the buffer (by simply dereferencing the pointer) is a valid menu selection, e.g.
#include <stdio.h>
#define MAXC 1024 /* read buffer max characters */
int main (void) {
int input = 0;
char buf[MAXC];
// domainEntry *myDomains = buildDomainDB();
for (;;) { /* loop continually until valid input or EOF */
fputs ("\nSelect top level domain:\n"
" 1-EDU\n"
" 2-COM\n"
" 3-ORG\n"
" 4-GOV\n"
" 5-MIL\n"
" 6-CN\n"
" 7-COM.CN\n"
" 8.CAN\n\n"
"choice: ", stdout);
if (!fgets (buf, MAXC, stdin)) {
fputs ("(user canceled input.)\n", stderr);
return 1;
}
if (*buf < '1' || '8' < *buf) { /* check 1st char, validate range */
fputs (" error: invalid input\n", stderr);
continue;
}
input = *buf - '0'; /* convert char to integer */
break;
}
printf ("\nvalid input: %d\n", input);
}
Of course if a key gets stuck and the user enters more than 1023
characters -- characters will remain in the input buffer. But a simple test of whether the last character is '\n'
and if not whether MAXC - 1
characters were read will let you know whether that is the case. Your choice, but fgets
provides a much easier implementation. Just remember -- don't skimp on buffer size. I'd rather have a buffer 10,000 bytes too long that 1-byte too short....
Related Topics
Is the Pimpl Idiom Really Used in Practice
Make_Unique and Perfect Forwarding
Which, If Any, C++ Compilers Do Tail-Recursion Optimization
Concurrency: Atomic and Volatile in C++11 Memory Model
How to Construct a C++ Fstream from a Posix File Descriptor
How Does C++ Handle &&? (Short-Circuit Evaluation)
Pure Virtual Destructor in C++
Function Parameter Evaluation Order
C++: Life Span of Temporary Arguments
Compilers and Argument Order of Evaluation in C++
How to Properly Delete Nodes of Linked List in C++
How to Have Functions Inside Functions in C++
How to Implement Big Int in C++
Finding C++ Static Initialization Order Problems
To_String Is Not a Member of Std, Says G++ (Mingw)
Optimizing Away a "While(1);" in C++0X
Is There Any Advantage of Using Map Over Unordered_Map in Case of Trivial Keys