0

It is a program to find out number of vowels, consonants, digits and whitespaces in a string to be input by the user. After compilation and during running fgets part is skipped. When I use scanf, the program works fine except that I can't input whitespace. Where does the problem lie? Please explain elaborately ( I am a newbie :-P) and possible remedies.

#include<stdio.h>
#include<string.h>
void main()
{
/*Getting the input*/
printf("How much long is your string?: ");
int n;
scanf("%d",&n);
int i,j,vowels=0,consonants=0,spaces=0,digits=0,actual_length;
char k, str[n+1];
printf("Please enter your string: ");
fgets(str,n+1,stdin);
actual_length=strlen(str);
/*Actual computation*/
for(i=0;i<actual_length;i++)
 {
  if(str[i]=='A'||str[i]=='E'||str[i]=='I'||str[i]=='O'||str[i]=='U')
  vowels++;
  if(str[i]=='a'||str[i]=='e'||str[i]=='i'||str[i]=='o'||str[i]=='u')
  vowels++;
  if(str[i]==32)
  spaces++;
  for(j='0';j<='9';j++)
   {
    if(str[i]==j)
    digits++;
   }
  for(k='A';k<='Z';k++)
   {
    if(k!='A'&&k!='E'&&k!='I'&&k!='O'&&k!='U')
     {
      if(str[i]==k)
      consonants++;
     }
   }
   for(k='a';k<='z';k++)
   {
    if(k!='a'&&k!='e'&&k!='i'&&k!='o'&&k!='u')
     {
      if(str[i]==k)
      consonants++;
     }
   }
 }
printf("The number of vowels are %d, number of consonants are %d, number of digits are %d and number of white spaces are %d\n",vowels,consonants,digits,spaces);
}
  • 7
    There must be a few duplicates of this. It has to do with the input buffering, and the fact that the Enter key you press to end the input for the `scanf`` call before is still in the input buffer. – Some programmer dude Oct 25 '16 at 11:03
  • related: http://stackoverflow.com/questions/5240789/scanf-leaves-the-new-line-char-in-buffer – Yağmur Oymak Oct 25 '16 at 12:06
  • As @Someprogrammerdude pointed out there is a newline character in stdin after entering "n". I just added "getchar();" before fgets() and it is working fine. – MayurK Oct 25 '16 at 12:14
  • "There must be a few duplicates", indeed. This is a super duper common problem for poor newbies trying to use scanf for input. – Steve Summit Oct 25 '16 at 15:22
  • Note: except for Visual Studio, the `main()` function must have a return type of `int`, not `void` – user3629249 Oct 26 '16 at 22:18
  • for ease of readability and understanding: 1) consistently indent the code. Indent after every opening brace '{'. Unindent before every closing brace '}'. Never use tabs for indenting as each wordprocessor/editor has the tab stops/tab width set for individual preferences. Suggest using 4 spaces for each indent level as that is readable even with variable width fonts. 2) follow the axiom: *only one statement per line and (at most) one variable declaration per statement.* – user3629249 Oct 26 '16 at 22:21
  • when calling any of the `scanf()` family of functions, always check the returned value (not the parameter values) to assure the operation was successful. – user3629249 Oct 26 '16 at 22:22
  • variable names should indicate `content` or `usage` (or better, both). `n` give absolutely not indication of either of these concepts. – user3629249 Oct 26 '16 at 22:24
  • the call to `scanf()` did not consume the trailing newline (appending a `%c` to the format string should handle that) so when the call to `fgets()` executed, the first thing is sees is the newline sequence and returns – user3629249 Oct 26 '16 at 22:25
  • suggest reading about `tolower()` and `isalpha()` and `isdigit()` and `isspace()` (all found in the header file: `ctype.h` Note: if it is alpha, and not a vowel, then it is a `consonant`. Usage of the preceding functions and logic will greatly simplify your code. Should probably be checking for punctuation also. – user3629249 Oct 26 '16 at 22:31
  • Note: many of the variables in the posted code can never be negative, so better to use `size_t` rather than `int` and print them with `%lu` rather than `%d` – user3629249 Oct 26 '16 at 22:34
  • Thank you @user3629249. Those were helpful tips. –  Nov 18 '16 at 14:16

1 Answers1

0

Your problem is that when you say

scanf("%d",&n);

it reads the number but not the newline. Then, when you call fgets, it reads the newline, and it thinks it's an empty line, and it never gets to the real string you wanted to read (that is, from the next line).

The general rule is, don't try to have a mixture of calls to scanf and fgets in one program like this -- you will always get tangled up in newlines, and it's a huge and pointless nuisance to try to untangle them.

One way of fixing the problem is to use fscanf to read the number, too:

printf("How much long is your string?: ");
char tmp_n_str[15];
fgets(tmp_n_str, sizeof(tmp_n_str), stdin);
int n = atoi(tmp_n_str);

The other way, which someone else will probably suggest, is to "flush the input" to get rid of the extra newline. You can do that, too, and it's certainly a popular technique, although personally I don't like it, for a number of reasons which I won't go in to here.

Steve Summit
  • 45,437
  • 7
  • 70
  • 103
  • Will it take care if string length is more than 15 characters? I didn't get it. – MayurK Oct 25 '16 at 12:21
  • @MayurK If the user asks for a string length like 1234567890123456789, it won't quite work, but that long a string would be unlikely to work anyway. The number 15 in my code is how long the *number* can be, not how long the actual string can be. – Steve Summit Oct 25 '16 at 12:25
  • @Steve Thanks. This way is fine for the given program. But what if I have to scan a char? What if I have to do multiple scans in a program? Should I write these 3 lines of code each time? Or should I write a function for this, and use it in each of my programs? Or should I use %*c? But that's not safe(buffer overflow protection). What do practical professional programmers do each time they have an urge to use scanf? And please demonstrate the use of fscanf. –  Nov 18 '16 at 15:09
  • @Xarn To be honest, I never even have the urge to use scanf. I always -- *always!* -- read lines using either `fgets` or my own `fgetline` function. I very often break the line into whitespace-separated "words" using my own `getargs` function. (The Standard function for doing that is `strtok`.) Then I might call `atoi` or `atof` on one or more of the "words". This might sound like a nuisance, but really, it's fine. (You asked specifically abut reading characters, and I'm not sure what you mean. If I'm reading individual characters I might well use `getc`.) – Steve Summit Nov 18 '16 at 15:26