-2

I'm solving CS50 (problemset 1) i.e water.c. It asks user to write a program that prompts the user for the length of his or her shower in minutes (as a positive integer) and then prints the equivalent number of bottles of water (as an integer). 1 min of shower = 12 bottles consumed MAIN PROBLEM: The problem is that we have to ensure that the user inputs a positive number of minutes otherwise it keeps on re-prompting his back to input/scanf statement. As long as he enters he enters length<=0, I can re-prompt him back using while(length<=0) condition but as he enters a character i.e abc123 in input my code keeps on executing. Any solutions??

>

 #include <stdio.h>
 int main()
 {   int length=0;
    int min=12;
    int bottle=0;
    printf("Enter length of his or her shower in minutes");
    scanf("%d", &length);
    while (length <= 0){
    printf("Enter length of his or her shower in minutes");
    scanf("%d", &length);
    }
    bottle= (min*length);
    printf("%d", bottle);

    return 0;
 }
Hamza Azam
  • 17
  • 1
  • 3
  • 10
  • 2
    Show us what you've done so far, don't just ask us to write code for you – MD XF Oct 19 '16 at 17:50
  • 1
    edited, now you can see my code :) – Hamza Azam Oct 19 '16 at 17:53
  • take a look at `strtol`. You can read the input as a string, use `strtol` to convert the string to an integer, and if there's any error during the conversion `strtol` will indicate it by setting errno: https://linux.die.net/man/3/strtol – yano Oct 19 '16 at 17:59
  • Possible duplicate: http://stackoverflow.com/questions/28911745/how-to-stop-infinite-loop-when-character-is-pressed-in-place-of- Try it out. – lu5er Oct 19 '16 at 18:02
  • [This answer](https://stackoverflow.com/a/23080439/3386109) may help. – user3386109 Oct 19 '16 at 18:45

4 Answers4

6

You can solve this by reading a string first, and then extracting any number:

#include <stdio.h>

int main(void)
{
    int length = 0;
    char input[100];
    while(length <= 0) {
        printf("Enter length: ");
        fflush(stdout);
        if(fgets(input, sizeof input, stdin) != NULL) {
            if(sscanf(input, "%d", &length) != 1) {
                length = 0;
            }
        }
    }

    printf("length = %d\n", length);
    return 0;
}

Program session:

Enter length: 0
Enter length: -1
Enter length: abd3
Enter length: 4
length = 4

Crucially, I always check the return value from scanf, the number of items successfully converted.

Weather Vane
  • 33,872
  • 7
  • 36
  • 56
  • I don't wanna be mean :D, but why `int main(int argc, char const *argv[])` If the program doesn't use those arg? Just asking. In my opinion should be `int main(void)` – Michi Oct 19 '16 at 18:32
  • @Michi oh ok, it was just my standard test file, edited. – Weather Vane Oct 19 '16 at 18:32
  • While it can be done with `scanf()` this method is generally far less problematic and probably simpler. – Clifford Oct 19 '16 at 18:53
2

If you don't care about Inputs like 1f then the Above Answers are ok For you, but if you do not want to accept this kind of Input, then the following approach does something like that:

#include<stdio.h>

int checkInput(void);
int main(void){
    int number = checkInput();

    printf("\nYour number is\t%d\n",number);

    return 0;
}

int checkInput(void){
    int option,check;
    char c;

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

        if(scanf("%d%c",&option,&c) == 0 || c != '\n'){
            while((check = getchar()) != 0 && check != '\n' && check != EOF);
            printf("\tI sayed a Number please\n\n");
        }else{
            if ( option < 1){
                printf("Wrong input!\n");
            }else{
                break;
            }
        }
    }while(1);

    return option;
}

Output:

Please type a number:   1f
    I sayed a Number please

Please type a number:   f1
    I sayed a Number please

Please type a number:   -1
    Wrong input!
Please type a number:   1

Your number is  1
Michi
  • 5,175
  • 7
  • 33
  • 58
  • @WeatherVane You right :D. But My Answer doesn't concentrate on that. I already said that is about inputs like `1f` or `f1`. And I hate to make my Answer a Tutorial as well :d. – Michi Oct 19 '16 at 18:46
  • `(check = getchar()) != 0` is curious. Why check against `0`? – chux - Reinstate Monica Oct 19 '16 at 20:21
  • @chux same like `(check = getchar()) != '\0'` or `(check = getchar()) != 1)` or `2`, `3`,`4`....`100`...`n`. I just use an extra check. – Michi Oct 20 '16 at 17:18
  • I see code is doing an extra check, but _why_ stop reading if a null character is encountered? Looks counter-productive. – chux - Reinstate Monica Oct 20 '16 at 17:23
  • @chux I used this Function together with others to read a file and there I had to check if a null character was encountered. Here is about reading Input from keyboard. Why should keep reading if the null character is present? How does even be possible? I know that `while((check = getchar()) != '\n' && check != EOF);` is fine here, more over I can remove `check` and use it like this `while((option = getchar()) != '\n' && option != EOF);` – Michi Oct 20 '16 at 17:31
  • @chux Should I check `c != EOF` here ==>> `if( (scanf("%d%c",&check,&c) == 0 || c != '\n') && c != EOF )`. ?...I'm asking because I already know that something is wrong, when the first check is `0` here `scanf("%d%c",&option,&c) == 0` – Michi Oct 20 '16 at 17:34
  • C uses null character in special way. That does not mean keyboards or re-direct input input `stdin` lack the ability to generate the null character. On some keyboard Ctrl @ works. For reading the rest of a _line_, read until `'\n'` or EOF is returned form `fgetc()`. – chux - Reinstate Monica Oct 20 '16 at 19:46
  • With `scanf("%d%c",&check,&c)`, no need to check if `c == EOF`. Check the return value of `scanf()`. 4 results possible. 2: number and following character found. 1: number and no more input available (or error). 0: input available but it is not a number. EOF: end-of-file or error occurred. Better to code `if((scanf("%d%*c",&check)==1) Success()`. Even better, uses [`fgets()`](http://stackoverflow.com/a/40139028/2410359) – chux - Reinstate Monica Oct 20 '16 at 19:51
  • @WeatherVane I know it is a late edit, but I made some changes to the code to meat the OP requirements. – Michi Dec 17 '17 at 15:24
  • 1
    This answer rejects an input such as `42*`, which my solution does not. – Weather Vane Dec 17 '17 at 19:00
1

You don't need the first prompt outside the loop because you have already initialised length to zero, so the loop will prompt at least once.

On most platforms other then Wndows, you need to flush stdout to show text not terminated with a newline.

scanf will return so long as a newline character is buffered and %d alone will not consume the newline, so you need to ensure that any remaining characters up to and including the newline are flushed to prevent an endless loop.

It is good practice to check the return value from scanf() since it makes no guaranteed about not modifying its arguments even when a conversion fails.

It is not clear why min is a variable here sine it is initialised but never re-assigned, but presumably that may be the case in the final program?

#include <stdio.h>

int main( void )
{   
    int length = 0 ;
    int min = 12 ;
    int bottle = 0 ;

    while( length <= 0 )
    {
        int converted = 0 ;

        printf( "Enter length of his or her shower in minutes: " ) ;
        fflush( stdout ) ;
        converted = scanf( "%d", &length ) ;
        if( converted != 1 )
        {
            length = 0 ;
        }

        while( (c = getchar()) != '\n' && c != EOF ) { } // flush line buffer
    }

    bottle = min * length ;
    printf( "%d", bottle ) ;

    return 0;
}
Clifford
  • 88,407
  • 13
  • 85
  • 165
  • `main` signature should be at least `int main(void){}` or `int main(int argc, char* argv[]){}` – Michi Oct 19 '16 at 18:24
  • The OP already explained in the Above Answer the problem `If you enter any char it runs infinite times.`. Your answer doesn't seems to fix this. More over the OP needs something like [This](http://ideone.com/u5MOy6) – Michi Oct 19 '16 at 18:26
  • `while( getchar() != '\n' ) { }` --> infinite loop on EOF. Although I doubt OP will close `stdin`. – chux - Reinstate Monica Oct 19 '16 at 18:29
  • @Michi : I merely copied the OPs framework - C11 makes that clear, C89 allows other implementation defined signatures, and I am will in to bet that any compiler will accept that. It is valid in C++ of course. – Clifford Oct 19 '16 at 18:31
  • @Clifford The only standard is `C11` Why should we speak about OLD STANDARD? – Michi Oct 19 '16 at 18:36
  • @Michi : The infinite loop is resolved by the getchar() loop. Yes it is possible for an EOF in environments supporting stdin redirection, but not likley for console input. This does not seem to be the kind of dialogue suited to file redirection input, but is easily fixed if you desire. Seems somewhat pedantic though. – Clifford Oct 19 '16 at 18:36
  • @Mich : The standard is the one applied by your compiler. All that legacy code still needs to compile, it is hardly cost effective to rewrite code just because some committee made a decision that might break years of work. I work in embedded systems development; there are plenty of niche platform compilers that do not support current standards and often have little need to do so. Moreover I work primarily in C++ and have not used C extensively since 2000 so freely accept that I am out-of-date. I don't think this discussion is particularly relevant. – Clifford Oct 19 '16 at 18:41
  • Added the EOF check in any case. – Clifford Oct 19 '16 at 18:44
  • @Clifford Sir, yes you right, but here we are speaking about the language and not about the compiler and the standard is `C11`. All others are only old standards. That was my point. – Michi Oct 19 '16 at 18:52
  • 1
    @Michi : You are of course right - since your suggested signature is valid regardless of version of the standard, it should be preferred for the greatest portability.. I was wary of adding code to the OP's that was not explicitly part of the solution as it might be a distraction. But we are already distracted. – Clifford Oct 19 '16 at 19:02
  • ... moreover, by the placement of variable declarations in the OP's original code I have assumed lowest common denominator C89. – Clifford Oct 19 '16 at 19:06
  • @Clifford I'm not an Expert of the Language (maybe I will never be :D) But I like to stick with the Language and its main Standard. I up voted your Answer because does that what the OP needs. – Michi Oct 19 '16 at 19:06
0
int min = 0;
do {
   printf("Enter minutes: ");
   scanf("%i", &min);
} while(min <= 0);
//programs resumes after this.
Clifford
  • 88,407
  • 13
  • 85
  • 165
Joe Torres
  • 95
  • 7
  • If you enter any char it runs infinite times. I want only positive integer value otherwise re prompt it ! – Hamza Azam Oct 19 '16 at 17:55
  • SO is not a Tutorial Site, you should explain something to make it an Answer and not a Tutorial. – Michi Oct 19 '16 at 18:11
  • @HamzaAzam Probabli [This is what you need](http://ideone.com/u5MOy6). Check the following [here](http://stackoverflow.com/questions/32671751/c-getchar-doesnt-wait-for-input-conditional-loop-doesnt-int/32671910#32671910) , [Here](http://stackoverflow.com/questions/34165592/scanf-skipped-after-reading-in-integer-in-c-in-while-loop/34165759#34165759) and [Here](http://stackoverflow.com/questions/31770861/validate-parameter-for-0-or-1/31772378#31772378). Should answer to all your Questions. – Michi Oct 19 '16 at 18:21
  • @Michi : Comments are not for posting answers and answers off-site are hardly helpful to the SO community. It will not show up in searches and cannot be voted or commented on. – Clifford Oct 19 '16 at 18:26
  • 1
    `scanf("%i", min);` ==>> `scanf("%i", &min);` – Michi Oct 19 '16 at 18:35