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?