Why Do I Get an Infinite Loop If I Enter a Letter Rather Than a Number

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

  1. (return == EOF) the user canceled input by generating a manual EOF by pressing Ctrl+d (or on windows Ctrl+z, but see CTRL+Z does not generate EOF in Windows 10 (early versions));
  2. (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' or EOF is found); and finally
  3. (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



Leave a reply



Submit