Hangman code in C - c

I am trying to write a hangman program in C.
Occasionally, some sentences appear twice in the program's output. The program is also intolerant of user inputs other than 1. How can I fix this?
Here's my code:
#include <stdio.h>
#include <string.h>
/* contant declarations */
#define NUM_TRIES_ALLOWED 10
int main() {
/* variable declarations */
int num_letters = 0, /* length of word char array */
count = 0, /* for word char array */
tries = 0, /* total tries user has used */
num_vis_chars = 0, /* # of visible characters */
correct_guesses = 0, /* # of correct guesses */
correct_flag = 0, /* was guess correct? */
repeat_flag = 0, /* was guess a repeat? */
choice;
char guess, guessword;
/* array declarations */
char word[255] = " ";
char incorrect_letters[255] = " ";
/* get word */
puts("Enter a word for player to guess.");
gets(word);
("Ready to start!\n");
num_letters = strlen(word);
char visible_word[num_letters]; /* displays correct guesses */
/* initialize visble_word */
for (count = 0; count < num_letters; count++)
visible_word[count] = '*';
visible_word[num_letters] = '\0';
if (guess == visible_word[count]) {
while (tries < NUM_TRIES_ALLOWED) {
printf("The word is: %s\n\n", visible_word);
printf("Number of turns remaining: %d", NUM_TRIES_ALLOWED - tries);
printf(
"\nWould you like to guess the word [w] or guess a letter [l]:");
choice = getchar();
if (choice == 'l') {
printf("\nWhat letter have you chosen?:\t ");
scanf(" %c", &guess);
}
/* match guess against previous guesses */
for (count = 0; count < num_letters; count++)
if (guess == visible_word[count]
|| guess == incorrect_letters[count]) {
repeat_flag = 1;
correct_flag = 1;
break;
}
if (repeat_flag == 0)
/* check for matches in string */
for (count = 0; count < num_letters; count++) {
if (guess == word[count]) {
visible_word[count] = guess;
correct_guesses++;
printf(
"\n**************************************************************\n\n");
printf("Good choice!\n\n");
if (correct_guesses == num_letters) {
puts("\n\nCONGRATULATIONS! You guessed the word!");
printf("WORD: %s\n\n", visible_word);
exit(0);
}
correct_flag = 1;
}
}
if (correct_flag == 0) {
incorrect_letters[tries] = guess;
tries++;
printf(
"\n**************************************************************\n\n");
printf("Bad choice!\n\n");
}
/* reset flags */
repeat_flag = 0;
correct_flag = 0;
}
puts("You did not guess the word.");
printf("WORD: %s\n\n", visible_word);
}
if (choice = 'w') {
printf("\nWhat word have you chosen?:\t ");
scanf("%s", &guessword);
if (guessword == word) {
printf("CONGRATULATIONS! You guessed the word!");
} else {
printf("nops");
}
}
return 0;
}

I would suggest providing functions for this code. The code is difficult to read and follow. Start with first creating a menu function, then get user input function; also do checks with-in the function that verify that the input if valid before it sends a character back. Then check to see if the guess is correct and post the response using a function. You will find that if you draw out your code and create the necessary functions it will be much easier to follow and debug.
print_menu() displays the options for the user and an exit choice.
get_user_input() gets the user input with checking to see that it is valid input
print_remaining() prints remaining and guessed characters of the word at play
Note if you for instance are getting a char from the user and they enter a letter.
scanf_s("%c", &guess, _countof(guess));
Note: _countof(ArrayName) simply just verifies that there is enough space for the placement of the char or string that is going to be assigned. Make sure to #include "stdlib.h" file for this function.
The reason for using scanf_s is that it reads over whitespaces. It dose not read the bad input and if you encounter some from the user, it puts the bad input back into the input buffer where it got if from. The scanf_s() has a return value and its value is going to be how many correct input responses were filled. Meaning this if %c actually read a char then the return value will be 1. if you got a 4 for the entry it will put the four back and return a 0. Good way to read input. getchar() also works well with this but has some detailed conditions that could cause some grief if not understood. Would be something to consider.
while(scanf_s("%c", &guess, _countof(guess)) != 1 )
// clear the input buffer and then try again.
Then you just clear the input buffer with
while(getchar() != '\n')
continue;
Above: Clears everything in the input buffer up to and including the newline char. Then, ask the user to enter valid input this time.
If you getting stuff that is printing twice that tells me that the loop is running past its exit condition. If your exit condition has anything to do with reading input. The above loop will take care of any information chillen in the input buffer. Hope this helps. Focus on only calling functions in the actual code. Makes debugging much easier.

Related

Why is my call to strcmp not working as expected?

I'm new to C and programming in general. It's a guessing game where the user has a certain amount of guesses to guess whatever the hidden message is.
Unfortunately, even if I enter the correct word, it does not register it as being correct.
#include <stdio.h>
#include <stdlib.h>
Lost(){
printf("You lost");
}
Won(){
printf("You won");
}
int main(void)
{
char HiddenMessage[7] = "Cameron";
char Guess[50] = "";
int Tries = 0;
int MaxTries = 5;
while(Tries != MaxTries && strcmp(HiddenMessage, Guess) != 0){
printf("Guess the secret word:");
scanf("%s", Guess);
Tries++;
if (Tries == MaxTries){
Lost();
}
else if (strcmp(HiddenMessage, Guess) == 0){
Won();
}
}
return 0;
}
How do I structure this to ensure that if a Guess occurs correctly within the number of tries it runs Won();
You should have received a warning about:
char HiddenMessage[7] = "Cameron";
if your compiler was doing its job properly :-)
Seven characters is not enough to store Cameron and the end-of-string marker. Try it again with:
char HiddenMessage[] = "Cameron";
This will make sure enough characters are set aside for the full string.
If you're interested, this is covered in C11 6.7.9 Initialization /14 (emphasis added):
An array of character type may be initialized by a character string literal or UTF−8 string literal, optionally enclosed in braces. Successive bytes of the string literal (including the terminating null character if there is room or if the array is of unknown size) initialize the elements of the array.
So what your code will give you is the sequence of characters { 'C', 'a', 'm', 'e', 'r', 'o', 'n' } but with no \0 at the end to make it a C string. That means strcmp cannot be guaranteed to give you the results you want (it will possibly run off the end of the array during the comparison).
And, as an aside, scanf with the unbounded %s format specifier is considered rather dangerous in non-trivial code, since there's no way to protect against buffer overflow.
If you're looking for a fairly bullet-proof user input function that can detect and mitigate this, have a look at this earlier answer of mine.
It allows prompting, prevents buffer overflow, properly handles line-based user input (no half lines delivered to user) and so on.
It's what I use for simple console applications (where no better user input method is available).
There are, essentially, four errors in your code:
As paxdiablo has stated, your HiddenMessage array is not large enough to hold the text you have given plus the nul terminator.
You need to define (or at least declare) your Lost and Won functions before you use them.
The definitions of these two functions are incorrect, as you have not specified a return type: void is suitable for functions that don't return anything.
You must include stdio.h to get the definitions of printf and scanf
Here's a fixed (and working) version of your code with the corrections made:
#include <stdlib.h>
#include <stdio.h> // MUST include this for "printf" and "scanf" functions
// You can keep these DEFINITIONS after "main" if you like, but then you'll need to
// have "pre-declarations" of them (the two commented lines immediately below).
// void Lost();
// void Won();
void Lost() {
printf("You lost");
}
void Won() {
printf("You won");
}
int main()
{
char HiddenMessage[8] = "Cameron"; // Added enough space for the nul terminator
char Guess[50] = "";
int Tries = 0;
int MaxTries = 5;
while (Tries != MaxTries && strcmp(HiddenMessage, Guess) != 0) {
printf("Guess the secret word:");
scanf("%s", Guess);
Tries++;
if (Tries == MaxTries) {
Lost();
}
else if (strcmp(HiddenMessage, Guess) == 0) {
Won();
}
}
return 0;
}
Feel free to ask for further clarification and/or explanation.
Another significant problem is your failure to validate any of the input. Any time (means ANY time) you take input, you must validate the input succeeds before relying on the data. Failure to validate invites undefined behavior.
Validations are simple, if you read input with a function check the return to validate you got all the input you were expecting. For example:
printf (" try no. %d - Guess the seceret word: ", tries + 1);
if (scanf ("%49s", guess) != 1) { /* read/validate word */
fputs ("(error: EOF encountered reading guess.)\n", stderr);
return 1;
}
(note: the use of the field-width modifier to limit the number of characters that can be read. Failing to control the number of characters than can be input will read to attempt to write beyond the bounds of your array if the user enters 50 (or more) characters.)
Your logic is a bit awkward. All you need to do is loop continually until the user either wins the game or exhausts the number of tries available. Rearranging your logic slightly, you could do something like:
(Edited to empty-stdin)
printf ("HANGMAN - you have %d tries to guess the word.\n\n", MAXTRIES);
for (;;) { /* loop continually until win or tries exhausted */
printf (" try no. %d - Guess the seceret word: ", tries + 1);
if (scanf ("%49s", guess) != 1) { /* read/validate word */
fputs ("(error: EOF encountered reading guess.)\n", stderr);
return 1;
}
if (strcmp (hiddenmsg, guess) == 0) /* compare hiddenmsg & guess */
break; /* if matched, break loop */
if (++tries == MAXTRIES) { /* test if all tries exhausted */
fputs ("\nyou have exhausted all guesses - you lost :(\n", stderr);
return 1;
}
/* empty stdin */
for (int c = getchar(); c != EOF && c != '\n'; c = getchar()) {}
}
puts ("\ncongratulations - you won.\n"); /* issue congrats & exit */
Putting it altogether in an example, you could do:
#include <stdio.h>
#include <string.h>
#define MAXTRIES 5
#define MAXGUESS 50
int main (void) {
char hiddenmsg[] = "Cameron",
guess[MAXGUESS] = "";
int tries = 0;
printf ("HANGMAN - you have %d tries to guess the word.\n\n", MAXTRIES);
for (;;) { /* loop continually until win or tries exhausted */
printf (" try no. %d - Guess the seceret word: ", tries + 1);
if (scanf ("%49s", guess) != 1) { /* read/validate word */
fputs ("(error: EOF encountered reading guess.)\n", stderr);
return 1;
}
if (strcmp (hiddenmsg, guess) == 0) /* compare hiddenmsg & guess */
break; /* if matched, break loop */
if (++tries == MAXTRIES) { /* test if all tries exhausted */
fputs ("\nyou have exhausted all guesses - you lost :(\n", stderr);
return 1;
}
/* empty stdin */
for (int c = getchar(); c != EOF && c != '\n'; c = getchar()) {}
}
puts ("\ncongratulations - you won.\n"); /* issue congrats & exit */
}
Example Use/Output
Typical try and fail game:
$ ./bin/hangman
HANGMAN - you have 5 tries to guess the word.
try no. 1 - Guess the seceret word: Alligator
try no. 2 - Guess the seceret word: Campers
try no. 3 - Guess the seceret word: Scam
try no. 4 - Guess the seceret word: Horses
try no. 5 - Guess the seceret word: Bananna
you have exhausted all guesses - you lost :(
A lucky win:
$ ./bin/hangman
HANGMAN - you have 5 tries to guess the word.
try no. 1 - Guess the seceret word: Lightning
try no. 2 - Guess the seceret word: Thunder
try no. 3 - Guess the seceret word: Geroge
try no. 4 - Guess the seceret word: Cameron
congratulations - you won.
(Testcase After Edit w/Example from Comment)
$./bin/hangman
HANGMAN - you have 5 tries to guess the word.
try no. 1 - Guess the seceret word: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxCameron
try no. 2 - Guess the seceret word: foo
try no. 3 - Guess the seceret word: Cameron
congratulations - you won.
Look things over and let me know if you have any further questions.
As strcmp compares the difference between each character till the end of string that is \0 So your buffer needs to be large enough to hold your string i.e. char HiddenMessage[8] = "Cameron";
PS you can avoid a lot of lengthy code
#include <stdlib.h>
int main()
{
char HiddenMessage[8] = "Cameron";
char Guess[50] = "";
int Tries = 0;
int MaxTries = 5;
while (Tries != MaxTries)
{
printf("Guess the secret word:");
scanf("%s", Guess);
Tries++;
if (strcmp(HiddenMessage, Guess) == 0)
{
Won();
return 0;
}
}
Lost();
return 0;
}
void Lost()
{
printf("You lost");
}
void Won()
{
printf("You won");
}
There are multiple problems with your code. The size of HiddenMessage reported by #paxdiablo is just one of them.
You should use fgets instead of scanf because scanf won't consume the newline. You will be stuck on the second iteration.
You increment Tries and test it against MaxTries before testing if the guess was correct. As a consequence the program will tell that the user lost before testing the validity of the last guess.
Once the user guessed the word and won, you must break from the while loop and terminate the program. With your code, after the program reported that the user won, it will ask for another guess if it wasn't the last guess.

Why is the following code not allowing me to get user input with fgets yet works with scanf?

This is a short excerpt from a bigger program, but the rest of the program is irrelevant since I think I was able to isolate the issue. I suspect that it has something to do with the way I'm using fgets. I've read that it's preferable to use fgets over scanf, but I can't seem to get it to work properly here. When I use the following code, the program doesn't give me a chance to enter the number (but simply skips to the while loop which checks if the number entered is in the correct range):
#include <stdio.h>
#include <stdlib.h>
#define SIZE 10
int main(void)
{
// ask user for how many items to store
printf("how many words would you like to enter? (1-%i): ", SIZE);
// save number of words user would like to store
char *input = malloc(sizeof(char));
fgets(input, 1, stdin);
// scanf("%c", input);
int words = atoi(input);
printf("the number of words is: %i\n", words);
while (words < 1 || words > SIZE)
{
printf("please enter a number between 1 and %i: ", SIZE);
scanf("%i", &words);
}
}
Here's the output I get:
~/workspace/extra_stuff/hash_tables/ $ ./test2
how many words would you like to enter? (1-10): the number of words is: 0
please enter a number between 1 and 10:
As you can see, it never let me enter the number, but simply moved on to the next step seemingly assuming I didn't enter anything.
If I change the code as follows, everything works as planned:
#include <stdlib.h>
#define SIZE 10
int main(void)
{
// ask user for how many items to store
printf("how many words would you like to enter? (1-%i): ", SIZE);
// save number of words user would like to store
char *input = malloc(sizeof(char));
// fgets(input, 1, stdin);
scanf("%c", input);
int words = atoi(input);
printf("the number of words is: %i\n", words);
while (words < 1 || words > SIZE)
{
printf("please enter a number between 1 and %i: ", SIZE);
scanf("%i", &words);
}
}
P.S.: I do realize that if using scanf, I could immediately store the input to an int variable w/o using atoi to convert char to int; however it seems that fgets requires a char *, so that's why I chose this route. Also, I realize that I'm supposed to free(input) later.
Can someone explain this behavior? Thanks.
EDIT:
Thanks to everyone who has replied so far! Some helpful suggestions there, but it looks like I'm having the same issue further in my program. Here's the code excerpt:
// ask for strings
for (int j = 0; j < words; j++)
{
char buffer[4096];
// fgets(buffer, 40, stdin);
// name=calloc(NAME_SIZE, sizeof(char));
// fgets(name, NAME_SIZE, stdin);
// printf("size of (array[j]->next)->text is: %lu\n", sizeof((array[j]->next)->text));
printf("please enter string #%i: ", j);
fgets(buffer, 4096, stdin);
printf("you've entered: %s", buffer);
int length = strlen(buffer);
printf("word length: %i\n", length);
}
When I run the program, it once again doesn't give me a chance to enter my input when it's supposed to:
please enter string #0: you've entered:
word length: 1
EDIT #2:
After working through David's answer and referencing other people's comments and other SO threads, I've come up with the following version of the code, which first asks the user for the number of words they'd like to enter (and validates the input) and then asks the user to enter those words (again, validating the input). It seems to be compiling w/o errors and warnings and functioning properly, though I am not 100% sure I've tested all the possible things that could go wrong with the user input, and there are some bits of the code I still don't completely understand (I'll list them below) -- if anyone has time/desire/patience to look through it and tell me if I can still improve something, please let me know. My goal is to use this code in another program that will ask for user input and store the entries in a hash table.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define BUF_SIZE_WORDS 4096
#define BUF_SIZE_NUMBERS 256
#define MAX_WORDS 10
int word_input(int num_words);
void empty_stdin();
int main(void)
{
int num_words = 0, /* number of words to enter */
word_count_check = 0; /* word count */
char buffer[BUF_SIZE_NUMBERS] = ""; /* buffer of sufficient size for input */
for (;;) /* loop continually until valid input of NUMBER OF WORDS USER WANTS TO ENTER or user cancels */
{
printf ("how many words would you like to enter? [1-%d]: ", MAX_WORDS);
// check for cancellation of input
if (!fgets (buffer, BUF_SIZE_NUMBERS, stdin))
{
fputs ("user canceled input\n", stderr);
return 1;
}
// check if user simply hit enter w/o typing anything
if(buffer[0] == '\n')
{
printf("please enter a value\n");
continue;
}
size_t inlength = strlen(buffer);
// validate length < BUF_SIZE_NUMBERS - 1
if (inlength >= BUF_SIZE_NUMBERS - 1)
{
fputs ("input exceeds allocated buffer size\n", stderr);
return 2;
}
if (inlength && buffer[inlength - 1] == '\n')
{
// printf("hurray!\n");
buffer[--inlength] = 0;
}
else if (inlength == BUF_SIZE_NUMBERS - 1) /* the line was too long */
{
printf("you've entered too many characters... please stick to a maximum of %i\n", BUF_SIZE_NUMBERS);
empty_stdin();
continue;
}
// make sure user actually entered a proper int
if (sscanf (buffer, "%d", &num_words) != 1) /* sscanf is used for conversion */
{
fputs ("invalid conversion to int; please provide valid input\n", stderr);
continue;
}
// check if the number entered is out of range
if (num_words < 1 || num_words > MAX_WORDS)
fprintf (stderr, "%2d out of valid range.\n", num_words);
else
break; /*if the input has been validated, we can now break out of the for loop */
}
// call the word_input function and store its return value in word_count_check
word_count_check = word_input(num_words);
// check if the number of words processed equals to the number requested by the user
if(word_count_check == num_words)
{
printf("success!\n");
}
else
{
printf("something went wrong, since word_count_check != num_words...\n");
}
}
int word_input(int num_words)
{
int word_count = 0;
for(;;) /* loop until word_count == num_words is achieved */
{
// declare an array for storing input string
char buffer[BUF_SIZE_WORDS];
char valid_input[BUF_SIZE_WORDS];
// prompt user for input
printf("please enter a string: ");
// get input and check for CTRL+D
if (!fgets(buffer, BUF_SIZE_WORDS, stdin))
{
fputs ("user canceled input\n", stderr);
exit(1);
}
// check if user simply hit enter w/o typing anything
if(buffer[0] == '\n')
{
printf("please enter a word that's more than 0 characters\n");
// empty_stdin();
continue;
}
size_t inlength = strlen(buffer);
// check if user input exceed buffer size
if (inlength >= BUF_SIZE_WORDS - 1)
{
empty_stdin();
fputs ("input exceeds allocated buffer size, please try again\n", stderr);
continue;
}
// check if the user entered too many characters
if (inlength == BUF_SIZE_WORDS - 1) /* the line was too long */
{
printf("you've entered too many characters... please stick to a maximum of %i\n", BUF_SIZE_WORDS);
empty_stdin();
continue;
}
if (inlength && buffer[inlength - 1] == '\n')
{
buffer[--inlength] = 0;
// get rid of trailing spaces using sscanf
sscanf(buffer, "%s", valid_input);
// figure out the length of the word the user entered
int word_length = ((int) strlen(valid_input));
printf("string length: %i\n", word_length);
// print out the word entered by the user one character at a time
printf("you've entered: ");
for (int i = 0; i < word_length; i++)
{
printf("%c", valid_input[i]);
}
printf("\n");
// increment word count
word_count++;
printf("word_count = %i\n", word_count);
if (word_count == num_words)
{
return word_count;
}
}
}
}
/* helper function to remove any chars left in input buffer */
void empty_stdin()
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
things I don't completely understand yet:
1)
if (!fgets (buf, MAXC, stdin)) { /* validate ALL user input */
fputs ("(user canceled input)\n", stderr);
return 1;
}
--- does this simply check if the user manually entered EOF (with ctrl+d), or does it check for something else too?
2) calling the empty_stdin() function below seemed to be causing some kind of a weird hang-up where it looked like the program was expecting further input from me as opposed to just going on to the next step, especially when I used it frequently (I figured why not clear the input stream every time the user types in something weird?) and/or when I decreased the buffer to something very small and then purposefully entered too many characters..
void empty_stdin()
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
3) eventually I want to use some of this code to load a dictionary from a text file (instead of user input) and store it in a hash table, and, in another version, in a trie. Besides using isalpha() to make sure we're only storing words that have letters in them, are there any other checks/validations that need to happen when processing the input, beside the ones above? Should any of the above checks be skipped?
There is no magic involved in dealing with strings in C -- but you do need to put your accounting hat on... Why? When dealing with input, you have to not only account for the number of characters that you place into your buffer (or wherever you are storing your input), but you also must account for the characters that remain in your input stream!
This is particularly true when using any of the scanf family of function for input. Why? Because on a matching or input failure, processing (reading and removing) characters from your input buffer (stdin here) stops, no further characters are read, and any character causing the matching failure remains unread in your input stream, just waiting to bite you again on your next attempted read.
Compounding this bewilderment for new C programmers is the fact that some conversion specifiers consume leading whitespace (e.g. space, tab, newline,...) and others do not. Your numeric conversion specifiers (along with "%s") consume leading whitespace while "%c" and "%[...]" do not.
All of which are the primary reasons new C programmers are encouraged to use line-oriented input functions like fgets or POSIX getline to handle user input (because they read the entire line at a time -- including the trialing '\n') freeing the new programmer for having to account for ending whitespace or offending characters not converted in the event of a matching failure...
Using fgets followed by a sscanf provides the additional benefit of allowing separate validation of (1) the read of input; and (2) the parse and conversion of input into the needed values.
(note: the only caveat with line-oriented input functions is that they read and include the trailing '\n' in the buffer they fill -- so you will need to "trim" the trailing whitespace as required. You don't want stray '\n' characters dangling off the end of the strings you are storing.)
That said, there will be times when reading input with the scanf family of functions makes sense. There is nothing wrong with doing so, so long as you validate the return every time and handle all three possible conditions:
the user presses ctrl+d on Linux to generate a manual EOF (ctrl+z on windoze);
you handle the matching or input failure cases, including removing any offending characters from your input buffer before your next attempted read; and finally
you have good input (the return indicates all the conversions anticipated, took place).
There is no magic to any of it, but it does take understanding the possible error conditions and handling each of them on every input.
In your case, let's look at your task of getting the number of words to enter from the user. Here you were attempting to read with fgets (that's good!), but you failed to provide sufficient storage to hold the input. When reading small amount of text from the user, a simple array with automatic storage type is all you need. However, you need to size the buffer accordingly (and do NOT skimp on buffer size).
There is no golden rule, but if I had users entering text to convert to a single number, then I would feel good with a 256 character buffer (which provides more than enough to hold the input of any valid number, plus another 230 some-odd characters to handle the time the cat steps on the keyboard, etc..)
For example, taking input from the user and getting the number of words to enter could be done in a manner similar to the following:
#include <stdio.h>
#define SIZE 10 /* good form defining a constant! */
#define MAXC 256 /* max characters for buffer */
int main (void) {
int nwords = 0, /* number of words to enter */
words = 0, /* each word */
wc = 0; /* word count */
char buf[MAXC] = ""; /* buffer of sufficient size for input */
for (;;) { /* loop continually until valid input or user cancels */
printf ("number of words to enter? [1-%d]: ", SIZE);
if (!fgets (buf, MAXC, stdin)) { /* validate ALL user input */
fputs ("(user canceled input)\n", stderr);
return 1;
}
/* validate length < MAXC - 1 and buf[length-1] == '\n' here */
if (sscanf (buf, "%d", &nwords) != 1) { /* sscanf for conversion */
fputs (" error: invalid conversion to int.\n", stderr);
continue;
}
if (nwords < 1 || SIZE < nwords) /* validate nwords in range */
fprintf (stderr, " %2d out of valid range.\n", nwords);
else /* good input received, break loop */
break;
}
(note: your while loop has been converted to a loop that will loop continually until valid input between 1 < value < SIZE is entered. The condition simply causes control to break; the loop at the point good input is received)
This loop presents a classic fgets/sscanf read and parse of information from the line of input entered by the user. You can parse the number from the line any way you like (but don't use atoi() -- it provides absolutely zero error checking of the conversion). You can use strtol (with proper validation) and you can simply use a pointer to walk-down-the-buffer, picking out digits, converting them from their ASCII to numeric value and then multiplying by 10 and adding as your go. Any way is fine so long as you validate, validate, validate each part of the operation.
Now turning to reading each of the words the user is supposed to enter, we will ignore conventional wisdom and use scanf for the task, but we will handle all three possible cases of the return every time. We will also add a counter to keep track of the valid inputs provided by the user and only exit the loop when we have that number of valid integers provided (or the user cancels by generating a manual EOF).
printf ("\nnumber of words entered: %d\n", nwords);
for (; wc < nwords;) { /* loop continually */
int rtn = 0; /* scanf return */
printf ("please enter a number between 1 and %d: ", SIZE);
rtn = scanf ("%d", &words); /* valdate ALL user input */
if (rtn == EOF) { /* handle EOF (manual) */
fputs ("(user canceled input)\n", stderr);
break;
}
else if (rtn == 0) { /* handle "matching failure" */
int c = getchar(); /* remove offending chars from stdin */
while (c != '\n' && c != EOF)
c = getchar();
fputs (" error: invalid integer input\n", stderr);
continue;
}
else { /* valid integer received */
int c = getchar(); /* remove any extra chars from stdin */
while (c != '\n' && c != EOF)
c = getchar();
if (words < 1 || SIZE < words) /* validate in-range */
fprintf (stderr, " %2d - invalid! (1 < valid < %d)\n",
words, SIZE);
else /* good input, increment word count */
printf (" word[%2d]: %3d\n", ++wc, words);
}
}
Note: the emptying of any offending characters from stdin can be turned into a convenient function so that you do not have to duplicate the loops each time you need to clear stdin during your input routine. You can replace it with a simple function, e.g.
/* helper function to remove any chars left in input buffer */
void empty_stdin()
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
That will help keep your code tidy. I'll let you incorporate that above.
Putting it altogether, you could do something like the following:
#include <stdio.h>
#define SIZE 10 /* good form defining a constant! */
#define MAXC 256 /* max characters for buffer */
int main (void) {
int nwords = 0, /* number of words to enter */
words = 0, /* each word */
wc = 0; /* word count */
char buf[MAXC] = ""; /* buffer of sufficient size for input */
for (;;) { /* loop continually until valid input or user cancels */
printf ("number of words to enter? [1-%d]: ", SIZE);
if (!fgets (buf, MAXC, stdin)) { /* validate ALL user input */
fputs ("(user canceled input)\n", stderr);
return 1;
}
/* validate length < MAXC - 1 and buf[length-1] == '\n' here */
if (sscanf (buf, "%d", &nwords) != 1) { /* sscanf for conversion */
fputs (" error: invalid conversion to int.\n", stderr);
continue;
}
if (nwords < 1 || SIZE < nwords)
fprintf (stderr, " %2d out of valid range.\n", nwords);
else
break;
}
printf ("\nnumber of words entered: %d\n", nwords);
for (; wc < nwords;) { /* loop continually */
int rtn = 0; /* scanf return */
printf ("please enter a number between 1 and %d: ", SIZE);
rtn = scanf ("%d", &words); /* valdate ALL user input */
if (rtn == EOF) { /* handle EOF (manual) */
fputs ("(user canceled input)\n", stderr);
break;
}
else if (rtn == 0) { /* handle "matching failure" */
int c = getchar(); /* remove offending chars from stdin */
while (c != '\n' && c != EOF)
c = getchar();
fputs (" error: invalid integer input\n", stderr);
continue;
}
else { /* valid integer received */
int c = getchar(); /* remove any extra chars from stdin */
while (c != '\n' && c != EOF)
c = getchar();
if (words < 1 || SIZE < words) /* validate in-range */
fprintf (stderr, " %2d - invalid! (1 < valid < %d)\n",
words, SIZE);
else /* good input, increment word count */
printf (" word[%2d]: %3d\n", ++wc, words);
}
}
}
Example Use/Output
$ ./bin/getintstdin
number of words to enter? [1-10]: five, maybe six?
error: invalid conversion to int.
number of words to enter? [1-10]: -2
-2 out of valid range.
number of words to enter? [1-10]: 3
number of words entered: 3
please enter a number between 1 and 10: two? three?
error: invalid integer input
please enter a number between 1 and 10: 2
word[ 1]: 2
please enter a number between 1 and 10: -2
-2 - invalid! (1 < valid < 10)
please enter a number between 1 and 10: 11
11 - invalid! (1 < valid < 10)
please enter a number between 1 and 10: 3
word[ 2]: 3
please enter a number between 1 and 10: 4
word[ 3]: 4
Note all the invalid inputs above and how the code handles each. So long as the input does not exceed 255-characters with fgets, the code will gracefully respond to inputs that are not valid integers (regardless how many are given) and it will respond to integer inputs that are out-of-range.
The code isn't much longer than the code you posted, but it addresses the possible error conditions that could arise and then handles the errors. When you boil it all down, that is what coding is all about. Look things over and let me know if you have further questions.

C Program - How to deny any non-numerical input

I've just started learning the language of C, and would love your help in cleaning up / simplifying my code if you know a better way to reach the following.
I want a program to ask for a number, and if that is found then proceed to print and end, however if anything else is put in (e.g. a letter key), then I want the program to loop asking for a number until one is given.
I started off by using a simple scanf input command, but this seemed to go into an infinite loop when I tried to check if a valid number (as we define them) was put in.
So instead I have ended up with this, from playing around / looking online, but I would love to know if there is any more efficient way!
//
// Name & Age Program
// Created by Ben Warren on 1/3/18.
//
#include <stdio.h>
int main (void)
{
//Setting up variables
int num;
char line[10]; /* this is for input */
//Collecting input
printf("Please enter any number? \t");
scanf("%d", &num);
//If Invalid input
while (num==0)
{
printf("\nTry again:\t");
fgets(line, 10, stdin); //turning input into line array
sscanf(line, "%d",&num); //scaning for number inside line and storing it as 'num'
if (num==0) printf("\nThat's not an number!");
}
//If Valid input
{
printf("\n%d is nice number, thank you! \n\n", num);
*}*
return 0;
}
Instead of checking if the value is different to 0, check the return value of
sscanf. It returns the number of conversions it made. In your case it should be 1. Unless the return value is 1, keep asking for a number.
#include <stdio.h>
int main(void)
{
int ret, num;
char line[1024];
do {
printf("Enter a number: ");
fflush(stdout);
if(fgets(line, sizeof line, stdin) == NULL)
{
fprintf(stderr, "Cannot read from stdin anymore\n");
return 1;
}
ret = sscanf(line, "%d", &num);
if(ret != 1)
fprintf(stderr, "That was not a number! Try again.\n");
} while(ret != 1);
printf("The number you entered is: %d\n", num);
return 0;
}
That is not a bad approach for someone new to C. One small improvement would be to actually check the return value of scanf(), since it returns the number of arguments successfully retrieved. Then you could get away from relying on num being 0 to indicate the input was valid. Unless you do want to specifically flag 0 as invalid input.
int ret = scanf("%d", &num);
ret == 1 would mean an integer was succesffully read into num, ret == 0 would mean it was not.
Consider using strtol to parse a string for a long int. This also allows you to detect trailing characters. In this example if the trailing character is not a newline, the input can be rejected. strtol can also detect overflow values. Read the documentation to see how that works.
#include <stdio.h>
#include <stdlib.h>
int main (void)
{
//Setting up variables
long int num = 0;
char line[40] = ""; /* this is for input */
char *parsed = NULL;
printf("Please enter any number? \t");
fflush ( stdout);
while ( fgets(line, 40, stdin))
{
parsed = line;//set parsed to point to start of line
num = strtol ( line, &parsed, 10);
if ( parsed == line) {//if parsed equals start of line there was no integer
printf("Please enter a number? \t");
printf("\nTry again:\t");
fflush ( stdout);
continue;
}
if ( '\n' != *parsed) {//if the last character is not a newline reject the input
printf("Please enter only a number? \t");
printf("\nTry again:\t");
fflush ( stdout);
}
else {
break;
}
}
if ( !parsed || '\n' != *parsed) {
fprintf ( stderr, "problem fgets\n");
return 0;
}
printf("\n%ld is nice number, thank you! \n\n", num);
return 0;
}
0 (zero) is a number...
But I see what you want to do...
You can check for a valid number, using isdigit or a combination of similar functions
I think its also important to follow the advice of other answers to use the return value from scanf using code such as:
int ret = scanf("%d", &num);
and examining ret for success or failure of scanf.

scanf validation sits and waits for another input. Why?

I was working on this sample exercise, and everything works as I would like it to, but there is one behavior I don't understand.
When providing input: if I make consecutive invalid entries everything seems to work great. But if I enter a number different from 1,2,3 in the case of the first question, or 1,2 in the case of the second question, the program just sits there until a new input is given. If another invalid entry is made, it goes back to the error "invalid entry" message, and if an appropriate number is entered, everything moves along fine.
I do not understand why it stops to wait for a second input...anyone?
Thanks guys.
#include <stdio.h>
static int getInt(const char *prompt)
{
int value;
printf("%s",prompt);
while (scanf("%d", &value) !=1)
{
printf("Your entry is invalid.\nGive it another try: %s", prompt);
getchar();
scanf("%d", &value);
}
return value;
}
int main() {
int wood_type, table_size, table_price;
printf("Please enter " );
wood_type = getInt("1 for Pine, 2 for Oak, and 3 for Mahogany: ");
printf("Please enter ");
table_size = getInt("1 for large, 2 for small: ");
printf("\n");
switch (wood_type) {
case 1:
table_price = (table_size == 1)? 135:100;
printf("The cost of for your new table is: $%i", table_price);
break;
case 2:
table_price = (table_size == 1)? 260:225;
printf("The cost of for your new table is: $%i", table_price);
break;
case 3:
table_price = (table_size == 1)? 345:310;
printf("The cost of for your new table is: $%i", table_price);
break;
default:
table_price = 0;
printf("The cost of for your new table is: $%i", table_price);
break;
}
}
You most likely need to flush your input buffer (especially with multiple scanf calls in a function). After scanf, a newline '\n' remains in the input buffer. fflush does NOT do this, so you need to do it manually. A simple do...while loop works. Give it a try:
edit:
static int getInt(const char *prompt)
{
int value;
int c;
while (printf (prompt) && scanf("%d", &value) != 1)
{
do { c = getchar(); } while ( c != '\n' && c != EOF ); // flush input
printf ("Invalid Entry, Try Again...");
}
return value;
}
The blank line you get if you enter nothing is the normal behavior of scanf. It is waiting for input (some input). If you want your routine to immediately prompt again in the case the [Enter] key is pressed, then you need to use another routine to read stdin like (getline or fgets). getline is preferred as it returns the number of characters read (which you can test). You can then use atoi (in <stdlib.h>) to convert the string value to an integer. This will give you the flexibility you need.
example:
int newgetInt (char *prompt)
{
char *line = NULL; /* pointer to use with getline () */
ssize_t read = 0; /* number of characters read */
size_t n = 0; /* numer of chars to read, 0 no limit */
static int num = 0; /* number result */
while (printf ("\n %s ", prompt) && (read = getline (&line, &n, stdin)) != -1)
{
if ((num = atoi (line)))
break;
else
printf ("Invalid Input, Try Again...\n");
}
return num;
}
If some invalid input is entered, it stays in the input buffer.
The invalid input must be extracted before the scanf function is completed.
A better method is to get the whole line of input then work on that line.
First, put that input line into a temporary array using fgets(),
then use sscanf() (safer than scanf because it guards against overflow).
#include <stdio.h>
int main(int argc, const char * argv[]) {
char tempbuff[50];
int result, d , value;
do
{
printf("Give me a number: ");
fgets( tempbuff, sizeof(tempbuff), stdin ); //gets string, puts it into tempbuff via stdin
result = sscanf(tempbuff, "%d", &value); //result of taking buffer scanning it into value
if (result < 1){ //scanf can return 0, # of matched conversions,
//(1 in this case), or EOF.
printf("You didn't type a number!\n");
}
}while (result < 1);
//some code
return 0;
}
Knowledge from: http://www.giannistsakiris.com/2008/02/07/scanf-and-why-you-should-avoid-using-it/

How to check if the user input an integer using scanf

I created a program to make a diamond out of *'s. I am looking for a way to check if the type of input is an integer in the C language. If the input is not an integer I would like it to print a message.
This is what I have thus far:
if(scanf("%i", &n) != 1)
printf("must enter integer");
However it does not display the message if it's not an integer. Any help/guidance with this issue would be greatly appreciated!
you can scan your input in a string then check its characters one by one, this example displays result :
0 if it's not digit
1 if it is digit
you can play with it to make your desired output
char n[10];
int i=0;
scanf("%s", n);
while(n[i] != '\0')
{
printf("%d", isdigit(n[i]));
i++;
}
Example:
#include <stdio.h>
#include <string.h>
main()
{
char n[10];
int i=0, flag=1;
scanf("%s", n);
while(n[i] != '\0'){
flag = isdigit(n[i]);
if (!flag) break;
i++;
}
if(flag)
{
i=atoi(n);
printf("%d", i);
}
else
{
printf("it's not integer");
}
}
Use fgets() followed by strtol() or sscanf(..."%d"...).
Robust code needs to handle IO and parsing issues. IMO, these are best done separately.
char buf[50];
fgets(buf, sizeof buf, stdin);
int n;
int end = 0; // use to note end of scanning and catch trailing junk
if (sscanf(buf, "%d %n", &n, &end) != 1 || buf[end] != '\0') {
printf("must enter integer");
}
else {
good_input(n);
}
Note:
strtol() is a better approach, but a few more steps are needed. Example
Additional error checks include testing the result of fgets() and insuring the range of n is reasonable for the code.
Note:
Avoid mixing fgets() and scanf() in the same code.
{ I said scanf() here and not sscanf(). }
Recommend not to use scanf() at all.
strtol
The returned endPtr will point past the last character used in the conversion.
Though this does require using something like fgets to retrieve the input string.
Personal preference is that scanf is for machine generated input not human generated.
Try adding
fflush(stdout);
after the printf. Alternatively, have the printf output a string ending in \n.
Assuming this has been done, the code you've posted actually would display the message if and only if an integer was not entered. You don't need to replace this line with fgets or anything.
If it really seems to be not working as you expect, the problem must be elsewhere. For example, perhaps there are characters left in the buffer from input prior to this line. Please post a complete program that shows the problem, along with the input you gave.
Try:
#include <stdio.h>
#define MAX_LEN 64
int main(void)
{ bool act = true;
char input_string[MAX_LEN]; /* character array to store the string */
int i;
printf("Enter a string:\n");
fgets(input_string,sizeof(input_string),stdin); /* read the string */
/* print the string by printing each element of the array */
for(i=0; input_string[i] != 10; i++) // \0 = 10 = new line feed
{ //the number in each digits can be only 0-9.[ASCII 48-57]
if (input_string[i] >= 48 and input_string[i] <= 57)
continue;
else //must include newline feed
{ act = false; //0
break;
}
}
if (act == false)
printf("\nTHIS IS NOT INTEGER!");
else
printf("\nTHIS IS INTEGER");
return 0;
}
[===>] First we received input using fgets.Then it's will start pulling each digits out from input(starting from digits 0) to check whether it's number 0-9 or not[ASCII 48-57],if it successful looping and non is characters -- boolean variable 'act' still remain true.Thus returning it's integer.

Resources