2

Hello i'm trying to use a do-while loop to check the input and repeat the prompt until the user types in a correct integer. So that's my code:

#include <iostream>
#include <stdio.h>
#include <ctype.h>    

int main ()
{

  int a;

  do
  {
     printf("Please type in your number: ");
  }while(scanf_s("%d", &a) == 0); 

  std::cin.get();
  std::cin.get();

  return 0;
}

Well it seems to work. When I type in a number the program runs correctly. But when I type in a letter an infinite loop starts. Sincerly I don't know where the problem is.

Billy ONeal
  • 104,103
  • 58
  • 317
  • 552
Ordo
  • 705
  • 8
  • 18
  • 29
  • 7
    `scanf` will keep trying to scan for an `int`, but a letter will make it stop, and that letter will remain in the buffer until it's consumed/flushed. A common issue - http://stackoverflow.com/questions/1716013/scanf-causing-infinite-loop – wkl Nov 10 '10 at 06:14
  • 5
    You probably want to choose EITHER the iostream library OR the cstdio library for i/o purposes. – Billy ONeal Nov 10 '10 at 06:18
  • 2
    Tagged with `visual-c++` because of use of the Microsoft specific `scanf_s` function. – Billy ONeal Nov 10 '10 at 06:22
  • Well i can't use "std::cin.get()" without the iostream and i don't know another way to hold the screen so that i can see the result. – Ordo Nov 10 '10 at 06:23
  • 2
    @Ordo: The equivalent to `std::cin::get` is [`getchar`](http://www.cplusplus.com/reference/clibrary/cstdio/getchar/) – Billy ONeal Nov 10 '10 at 06:37
  • +1 because the question was asked half an hour ago and you have two wrong answers. – Billy ONeal Nov 10 '10 at 06:41

7 Answers7

4

Again, I suggest reading a line into a string and then trying to parse that string according to your needs. If the parse fails, simply prompt the user again. You can bury the messy details in a function template:

#include <iostream>
#include <sstream>
#include <string>

template <typename T>
T read(std::string prompt)
{
    for (; ;)
    {
        std::cout << prompt;
        std::string line;
        getline(std::cin, line);
        std::istringstream ss(line);
        T x;
        if ((ss >> x) && (ss >> std::ws).eof()) return x;
    }
}

int main ()
{
    int a = read<int>("Please type in your number: ");
    std::cout << "You entered " << a << '\n';
}
fredoverflow
  • 256,549
  • 94
  • 388
  • 662
  • 1
    That's nice and thorough, but paints a gloomy picture of C++ usability... :-. – Tony Delroy Nov 10 '10 at 07:02
  • Thanks for your advise but that wonderful piece of code is just to complicated for me. I'm just a beginner and i don't understand most of it. – Ordo Nov 11 '10 at 05:54
3

Here's what's going on -- I'll go through step by step. Starting from the do:

  1. output: Please type in your number:
  2. call to scanf Scanf finds that stdin is empty, and therefore waits for a line to be typed in.
  3. input : letter (note that the input buffer now contains "letter")
  4. scanf attempts to parse the string as an integer. Parsing fails before it consumes any characters. Therefore the buffer still contains "letter"
  5. scanf returns EOF (error)
  6. output: Please type in your number:
  7. call to scanf -- scanf sees that there's already waiting input in stdin
  8. scanf attempts to parse the buffer as an integer.....

This will go on forever because scanf will never consume the characters from the buffer. You can solve the problem by correctly checking for an error return code from scanf.

Billy ONeal
  • 104,103
  • 58
  • 317
  • 552
  • -1 I'm afraid. `scanf_s()` will never return EOF because there is always still a character in the buffer -- it will return 0, because that was the number of fields successfully converted. Also you don't say how to overcome this case -- you will need to read characters (e.g. with `getchar()` in a loop) until `\n` or EOF is seen. – j_random_hacker Nov 10 '10 at 07:02
  • @j_random_hacker: I believe I did not say `scanf_s` in my answer. I believe I said `scanf`, which returns EOF upon any failure, including "failures" that are not EOF. – Billy ONeal Nov 10 '10 at 15:32
  • Both functions (`scanf()` and `scanf_s()`) return EOF only when attempting to read a character results in EOF on the input stream or some other filesystem-level error. If characters can be read but don't match the next input field, this is regarded as "success" and the number of fields successfully read (possibly 0) is returned. The docs I found (e.g. http://www.cplusplus.com/reference/clibrary/cstdio/scanf/) don't make it as clear as I've tried to here, but the fact that they say that `scanf()` is explicitly allowed to return 0 implies this. – j_random_hacker Nov 10 '10 at 19:30
  • I verified this by looking at `scanf("%d", &x)`'s return value in 2 different cases: (1) User types 'A' -> result 0. (2) Input redirected from /dev/null -> result -1 (== EOF). Same answer from MSVC++2010 and g++ 4.1.2 on Linux. – j_random_hacker Nov 10 '10 at 19:34
0

@ ordo

Blockquote

I have modified my code so that it works now. But sincerly it just works for numbers and letters. I want it to work with every char. For example "!?%". I have already tried to change the "isalnum" by "isascii" but that does not work.

Blockquote

You can use

if(userInput>='!'&& userInput<= '~') // refer ASCII chart between !and ~. { exit=0; }

http://www.cdrummond.qc.ca/cegep/informat/professeurs/alain/images/ASCII1.GIF

Osaid
  • 557
  • 1
  • 8
  • 23
0

First of all never ever use scanf as its one hell of a dangerous function.

If you want to stick with C you should use fgets to read the input from the user to a buffer and then atoi to convert the input from the user to an integer.

Note: fgets always adds the 'enter' in to the buffer so you want to strip it off before converting the content of the buffer.

this could be easily done as follow:

_buffer[strlen(_buffer)-1] = '\0';
Marek Szanyi
  • 2,348
  • 2
  • 22
  • 28
  • There's no reason to avoid `scanf` unless you are trying to read strings with it. For things like reading numbers it's perfectly fine. – Billy ONeal Nov 10 '10 at 06:38
  • `_buffer[strlen(_buffer)-1]` is only valid in C99 which is not supported by the OP's compiler (Microsoft Visual C++) – Billy ONeal Nov 10 '10 at 06:41
  • 1
    @Billy ONeal: Agree with your 1st comment, but the 2nd...? That construction is unsafe because it's possible to read a string of length 0, but there's no problem with "compiler support" AFAICT. – j_random_hacker Nov 10 '10 at 06:48
  • 1
    @j_random_hacker: The code psicho proposes chooses an array size at runtime. That is a C99 feature (variable length arrays). It's not valid in C89, and would require dynamic allocation instead. – Billy ONeal Nov 10 '10 at 06:49
  • @j_random_hacker: Hmm.. upon rereading it doesn't look like a declaration anymore. Doh! @psicho sorry for the brain fart on that one. – Billy ONeal Nov 10 '10 at 06:50
  • 2
    @Billy: you can provide upper-bounds on data to read into strings - nothing inherently unsafe about it then either - just that too many people don't bother to be that careful. – Tony Delroy Nov 10 '10 at 06:51
  • @Tony: Wasn't aware of that. It's been a long time since I've messed with `cstdio` :) – Billy ONeal Nov 10 '10 at 06:51
  • 1
    @Billy ONeal: It's pretty obviously not a *declaration of* an array but an *assignment to* an element of a pre-existing array. (Which carries buffer overrun and, here, underrun risk -- but no language issues.) – j_random_hacker Nov 10 '10 at 06:52
  • @j_random_hacker: I believe I already acknowledged by brain fart lol. – Billy ONeal Nov 10 '10 at 06:55
  • @Billy ONeal: I just didn't see the comments update in time, no worries :) – j_random_hacker Nov 10 '10 at 06:57
  • how exactly can you read a 'string' of 0 length when you use fgets from a stdin ? the length of the string will always be at minimum 1. – Marek Szanyi Nov 10 '10 at 07:05
  • @psicho: stdin can be redirected to come from a zero-length file or socket; or the user can press Ctrl-D (Ctrl-Z in Windows). – j_random_hacker Nov 10 '10 at 07:10
0

I have modified my code so that it works now. But sincerly it just works for numbers and letters. I want it to work with every char. For example "!?%". I have already tried to change the "isalnum" by "isascii" but that does not work.

#include <stdio.h>
#include <ctype.h>

int main ()
  {

    int a;
    int b = 1;
    char c ;

    do
    { 
      printf("Please type in a number: ");

      if (scanf("%d", &a) == 0)
      {
        printf("Your input is not correct\n");
        do 
        {
          c = getchar();
        }
        while (isalnum(c));  
        ungetc(c, stdin);    
      }
      else
      { 
      printf("Thank you! ");
      b--;
      }

    }
    while(b != 0); 

    getchar();
    getchar();

    return 0;
  }
Ordo
  • 705
  • 8
  • 18
  • 29
-1
int main ()
{    
  int a;
  char userInput,exit=1;

  do
  {
     printf("Please type in your number: ");
     userInput=getch();

     if(userInput=='1') // suppose the correct input is 1.
     { exit=0; }         

  }while(exit); 

  std::cin.get();
  std::cin.get();

  return 0;
}

If the input is between 0 and 9...

 if(userInput>='0'&& userInput<= '9') // suppose the correct input is 1.
    { exit=0; }

Note that we have to use ' ' signs

Austin Henley
  • 4,625
  • 13
  • 45
  • 80
Osaid
  • 557
  • 1
  • 8
  • 23
-2

You can use getchar() function

do
  {
     printf("Please type in your number: ");
  }while((getchar() - '0') == 0); 
anand
  • 11,071
  • 28
  • 101
  • 159
  • Why - '0' == 0? How is that different than just `getchar() == '0'`? More importantly, this fails for numbers which contain more than one digit. – Billy ONeal Nov 10 '10 at 06:38
  • That would keep looping until they typed in the character '0' (of course it has line buffering issues, so they might need to hit return too). I think the post wants to get any number (e.g. 0, 1, 12, -423). – Tony Delroy Nov 10 '10 at 06:39
  • @Tony: What if my number is 101? – Billy ONeal Nov 10 '10 at 06:40
  • @Billy: sorry, my comment wasn't addressing yours - I should have prefixed my comment with @Alien01, but like you I started writing it thinking it'd be the first comment on the proposed answer. – Tony Delroy Nov 10 '10 at 06:49