Error in C with word counting program using switch statement - c

I am having trouble counting the number of words that the user enters using a switch statement. I want to ensure that multiple presses of the keyboard (ASCII,32) do not count for multiple words. As you can see, I have a memory variable that stores the previous keyboard press, and I want to only count a word when the current keyboard press is a space, and the previous press (i.e. the memory variable is not a space). The logic makes perfect sense to me; however, the code does not seem to work. Could someone please explain where my flaw in logic is? Thanks.
// 1.4 Exercise 1: output the amount of characters, number of words and number of newlines typed by the user
// Note that the escape command on my mac is ENTER then COMMAND Z
#include <stdio.h>
int main()
{
long characters = -1; // to exit program on my mac you must press COMMAND+Z
long newlines = 0;
long words = 1; // assume the document starts with a word
printf("Type stuff! To exit type CTRL+D (some computers use CTRL+Z then space)\n");
int i = 0;
int memory = 32; //stores the previous keyboard press
while (i != EOF) // characters will continue counting as long as the escape sequence is not entered
{
i = getchar(); //Assign the integer output of the buffered getchar() function to i
if (i != 10)
{
characters++;
}
switch (i)
{
case 10: // for enter key press
{
newlines++;
break; // exit switch (each character is unique)
}
case 32: // for space
{
if (memory != 32) // if the previous character press was not a space, then words++
{
words++;
}
memory = i; // stores the previous keyboard press
break; // exit switch (each character is unique)
}
default: break; //exit switch is the default action if the previous switches are not true
}
}
printf("%d characters were typed\n", characters);
printf("%d words were typed\n", words);
printf("%d lines were typed\n", newlines);
return 0;
}

You are only updating the value of memory when inside of your case statement when i = 32. With this, the value of memory will always be 32. Instead, you should update the value of memory after the switch statement is closed. i.e.
switch(i)
{
/* code here */
}
memory = i
As a side note, it will make your code more readable and aid portability if you don't hard-code the ascii values of characters into your switch statement. You can instead write them using the character.
switch(i)
{
case '\n':
/* code */
break;
case ' ':
/* code */
break;
}

You unconditionally have to store the previous character:
memory = i;
Also, think about the corner-cases, e.g. what happens when there's only a single word (without spaces) in a line. As you can see, not only spaces can separate words.

Related

Problem in program to count number of words, sentences and letters in C

I needed to make a program to count the number of words, sentences and letters by getting an input from the user. The program works perfectly until the input i give is multi-lined. If the input is longer than the text that can fit in the terminal window, the program starts to ignore all full stops/question marks/exclamation marks. I don't know why, and I'd like some help. This doesn't happen if the text can fit in one line of the terminal window. I also printed every character when it's read by the program, but that also ignores all full stops/question marks/ exclamation marks. None of those characters get printed. For clarification, a sentence is just the number of full stops/question marks/ exclamation marks, number of words is just the number of spaces in the text plus 1. Here is my code:
#include <stdio.h>
#include <ctype.h> //for the isalpha() function
#include <cs50.h> //for the get_string() function
int main(void)
{
int sentences = 0, letters = 0;
int words = 1;
char character;
string text = get_string("Enter Text: \n");
char x = 0;
while (text[x] != '\0')
{
character = text[x];
switch (character)
{
case ' ':
words++;
break;
case '.':
sentences++;
break;
case '?':
sentences++;
break;
case '!':
sentences++;
break;
default:
if (isalpha(character))
{
letters++;
}
}
x++;
}
printf("\n");
printf("WORDS: %d, LETTERS: %d, SENTENCES: %d\n", words, letters, sentences);
}
I'm fairly new to c, but I have around a year of experience in Python. Thank you for your time.
I’m going to make a few suggestions.
First, don’t use get_string1 (or scanf, or fgets). For a filter program like this, you don’t actually need to store the input in order to process it; use getchar (or fgetc) to read one character at a time and loop based on that:
int c; // getchar returns int, not char
...
puts( "Enter Text:" );
while ( ( c = getchar() ) != EOF )
{
// test c instead of text[x]
}
This approach will handle input of any length (such as if you redirect a file as your input), and it avoids the potential overflow issue Weather Vane identified in the comments. The downside is that you’ll have to manually signal EOF from the console for interactive input (using either Ctrl-z or Ctrl-d depending on your platform).
You can collapse some of your tests in your switch, such as
case '.' : // Each of these cases "falls through"
case '!' : // to the following case.
case '?' :
words++; // the end of a sentence is also the end of a word
sentences++;
break;
You’ll want to add cases to handle newlines and tabs:
case ' ' :
case '\n' :
case '\t' :
words++;
break;
except you don’t want to bump the words counter for repeating whitespace characters, or if the previous non-whitespace character was a punctuation character. So you’ll want an extra variable to track the class of the previously-read character:
enum {NONE, TEXT, PUNCT, WHITE} class = NONE;
...
while ( ( c = getchar() ) != EOF )
{
switch( c )
{
case ' ' :
case '\n' :
case '\t' :
if ( class == TEXT )
words++;
class = WHITE;
break;
case '.' :
case '!' :
case '?' :
if ( class == TEXT ) // Don’t bump the word counter
words++; // if the previous character was
// was whitespace or .! ?
if ( class != PUNCT ) // Don’t bump the sentence counter
sentences++; // for repeating punctuation
class = PUNCT;
break;
...
}
}
There will still be weird corner cases where this won’t give a completely accurate count, but should be good enough for most input.
You should be able to figure out the rest from there.
The CS50 string handling and I/O routines like get_string are pretty slick, but they grossly misrepresent how C actually does things. The string typedef is especially egregious because what it aliases is not a string. Just be aware these tools will not be available outside the CS50 curriculum, so don’t become too reliant on them.

Do while loop not exiting despite expression becoming false

I've got a program here which contains a do-while loop within a specified void method. I'm trying to exit the loop within the function, so that the do-while loop actually works as it is supposed to. Except after I run the program and one of the cases occurs, the program continues to run despite my while statement stating that it should only work while(userInput != 1).
I cannot use global variables to solve this problem, as my assignment limits me on using such techniques, thus any help would be much appreciated!
Here is a snippet of my code:
void functionTest()
{
int gameOver = 0;
int userInput;
do
{
printf("please enter a number 1-3");
scanf("%d",&userInput);
switch(userInput)
{
case 1:
printf("You entered %d",userInput);
gameOver = 1;
break;
case 2:
printf("You entered %d",userInput);
gameOver = 1;
break;
case 3:
printf("You entered %d",userInput);
gameOver = 1;
break;
}
}
while(gameOver!= 1);
}
}
The problem probably lies when you use scanf(). Something that you're inputting before hitting enter is not 1, 2 or 3. Could you tell us exactly what you type when it asks you to enter a choice?
Sometimes, the standard output needs to be flushed before using a fresh scanf(). Try fflush(stdout) before the scanf line.
See older question 1 and older question 2.
EDIT:
I can reproduce the problem easily enough if I enter anything apart from "1","2" or "3"...
I would suggest, you do the following before executing the switch statement:
Add fflush(stdout) before scanf()
Accept the input as a string (%s) instead of a number. (char [] needed)
Trim the string of trailing and leading white spaces.
Convert to number using a library function
Then switch-case based on that number
The problem is that if other characters (that aren't part of an integer) are present in the input stream before an integer can be read, scanf() fails and unusable data is never cleared out... which leads to an infinite loop (where scanf() repeatedly fails to read the same characters as an integer, over and over).
So you need to read off the invalid characters when scanf() fails, or as part of the format.
A simple fix would be to change your scanf from:
scanf("%d",&userInput);
to:
scanf("%*[^0-9]%d",&userInput);
to read (and discard) any characters in the input stream that aren't digits 0-9 before reading your integer... but that still doesn't check whether scanf fails for any other reason (like a closed input stream).
You could replace it with something like this:
int scanfRes,c;
do {
scanfRes = scanf("%d",&userInput); /* try to read userInput */
/* ..then discard remainder of line */
do {
if ((c = fgetc(stdin)) == EOF)
return; /* ..return on error or EOF */
} while (c != '\n');
} while (scanfRes != 1); /* ..retry until userInput is assigned */
..which will retry scanf() until the field is assigned, discarding the remainder of the line after each attempt, and exiting the function if fgetc() encounters an error or EOF when doing so.

C: Scanning from stdin

I'm writing code that, when called from the command line, is redirected a file. The lines of the file (which are sent over stdin) are parsed and read. I want to be able to call a function and have it scan an int, but it seems that there are issues with residual data in scanf (I don't actually know if that's the issue, but that's all I can think of). Here is my code:
dataSetAnalysis(data, experiments);
int selection;
while(1){ //always true. The loop only breaks when the user inputs 4.
printf("DATA SET ANALYSIS\n"
"1. Show all the data.\n"
"2. Calculate the average for an experiment.\n
"3. Calculate the average across all experiments.\n
"4. Quit.\n"
"Selection:__");
switch(selection){
case 1:
displayAll(d,e);
break;
case 2:
individualAverage(d,e);
break;
case 3:
allAverage(d);
break;
case 4:
exit(0);
}
scanf("%d", &selection);
}
And this is the second half of the main method.
while(fgets(currentLine, 20, ifp) != NULL){ //while there is still data in stdin to be read
experiments[i] = currentLine; //experiment[i] points to the same value as current line. Each value in experiments[] should contain pointers to different positions in the allocated buffer array.
currentLine += 20; //currentLine points 20 characters forward in the buffer array.
int j = 0; //counter for the inner while loop
while(j<10){ //while j is less than 10. We know that there are 10 data points for each experiment
scanf("%d ", &intBuffer[j]);
data[i][j] = intBuffer[j];
j++;
}
numExperiments++; //each path through this loop represents one experiment. Here we increment its value.
i++;
}
The program loops infinitely when reaching the while loop in dataSetAnalysis() and continues printing "DATA SET ANALYSIS...." without ever stopping to accept more input over stdin. Is the problem with scanning to the selection variable?
The problem is that your stdin is not clear, You have to clear your input buffer by iterating till you find an '\n' or an enter hit.
Try use this
while('\n' != getchar())
{ }
just before you scanf , it will get rid of the infinite loop
something like
while('\n' != getchar())
{}
scanf("%d", selection);
The first and fundamental rule is 'always check the return value from an input function' (such as scanf()). They always return some indication of whether they were successful or not, and if they're unsuccessful, you should not use the values that would have been set had the function call been successful.
With scanf() et al, the correct way to use the function is to test that you got the expected number of values converted:
if (scanf("%d", &intBuffer[j]) != 1)
…handle error…
You also have the format string: "%d " in the code. That doesn't stop until you type a non-white space character after the number. This is confusing, but any white space (blank, tab, newline) in a format string means read optional white space, but the function only knows when it has finished reading white space when a non-white space character is entered.

Putchar and Getchar in C

I'm reading K&R's The C Programming Language and have become confused on putchar and getchar. I made a program where you enter 10 chars and the program prints them back out to the screen.
#include <stdio.h>
int main()
{
int i;
int ch;
for(i = 0; i < 10; i++)
{
printf("Enter a single character >> ");
ch = getchar();
putchar(ch);
}
return 0;
}
I expected to get an output like this:
Enter a single character >> a
a
Enter a single character >> b
b
...and so on 10 times but this is the output I got: (I stopped after entering 2 chars)
Enter a single character >> a
aEnter a single character >>
Enter a single character >> b
bEnter a single character >>
Enter a single character >>
not sure why my input character is being combined with the fixed string and being output.
Also, I'm not too sure why ints are used to store characters.
putchar(ch);
just prints single character and the following printf continues within the same line. Simply add:
putchar('\n');
right after putchar(ch);, which will explicitly start the new line before the printf is executed. Additionally you should also take '\n' from the input which stays there after you enter the character:
for(i = 0; i < 10; i++)
{
printf("Enter a single character >> ");
ch = getchar();
getchar(); // <-- "eat" new-line character
putchar(ch);
putchar('\n'); // <-- start new line
}
You are not printing a new line. After putchar(ch); you should use putchar('\n'); to print a new line.
User terminal can operate in canonical and non-canonical modes. By default it operates in canonical mode and this means that standard input is available to a program line-by-line (not symbol-by-symbol). In question user inputs something (let it be letter 'a', 0x61 in hex) and pushes enter (new line character '0x0A' in hex). Ascii table is here. So this action gives a two symbols to a program. As mentioned in man getchar() reads it symbol-by-symbol. So loop iterates twice for one character. To see what is going on use the following program (+loop counter output, +character code output):
#include <stdio.h>
#include <unistd.h>
int main()
{
int i;
char ch;
for(i = 0; i < 10; i++)
{
printf("Enter a single character %d >>", i);
ch = getchar();
printf("Ch=0x%08X\n", ch);
/*putchar(ch);*/
}
return 0;
}
Output:
┌─(02:01:16)─(michael#lorry)─(~/tmp/getchar)
└─► gcc -o main main.c; ./main
Enter a single character 0 >>a
Ch=0x00000061
Enter a single character 1 >>Ch=0x0000000A
Enter a single character 2 >>b
Ch=0x00000062
Enter a single character 3 >>Ch=0x0000000A
Enter a single character 4 >>^C
So program gets two symbols and prints them. And new line symbol is not visible. So in the question user see one strange additional line.
Detailed description on different terminal modes and how to make its adjustments can be found here.
Also stty utility can be useful while working with terminal options ("icanon" tells if terminal use canonical mode or not).
And about storing chars as int in getchar() output - see my answer for similar topic.
The term on which our focus should be on, is "Stream".
A "Stream" is a like a bridge, responsible for flow of data in a sequential way. (The harmony of smooth streaming, both in and out of a program, is managed by libraries/header files, e.g. stdio.h)
Coming back to your question :
When you type input as 'a' and hit 'enter', you supply 2 values to input stream.
- a (ASCII Value : 97)
- enter (ASCII Value : 13)
/*This 'enter' as an input is the devil. To keep it simple, i will keep calling it
as Enter below, and the enter is usually not displayed on screen*/
NOTE/IMPORTANT/CAUTION before proceeding: Till the time your stream doesn't get completely empty, you can't write new characters from the console into the stream. (This scenario only implies for the use of getchar and putchar, as shown below)
HERE IS YOUR CODE:
for(i = 0; i < 10; i++)
{
printf("Enter a single character >> ");
ch = getchar();
putchar(ch);
}
Loop Pass 1 :
a) You ask user to enter a character. // printf statement
b) getchar reads only a single character from stream.
c) putchar renders/displays only a single character from stream.
d) At first pass you provide input as 'a' but you also hit 'Enter'
e) Now, your stream is like a ***QUEUE***, at first pass, and at 1st place of
the queue, you have 'a' and at 2nd place 'enter'.
f) Once you do putchar, the first character , i.e. 'a' from the stream/queue
gets displayed.
e) Loop ends.
g) Output of this pass:
Enter a single character >>a
Loop Pass 2 :
a) You ask user to enter a character. // printf() statement
b) Unfortunately your stream isn't empty. It has an "enter" value from the
previous pass.
c) So, getchar(), reads the next single character, i.e. 'enter' from stream.
(This is where you were expecting to manually enter the next character,
but the system did it for you. Read the NOTE/IMPORTANT/CAUTION section
mentioned above)
d) putchar() displays 'enter' on screen, but since 'enter' is no displayable
thing, nothing gets displayed.
e) Output of this pass:
Enter a single character >>
Loop Pass 3 :
Similar as loop 1, only input this time is 'b'.
Loop Pass 4:
Similar as loop 2
and so on till 10 passes. (So, last character you will be able to enter is 'e'.)
INFERENCE/CONCLUSION:
So, long story short, you were expecting to enter the next character,
so that getchar would pick your entered value, but since from your
previous pass,'enter' value was already waiting in the stream,
it got displayed first, giving you such an illusion.
Thank you.
Let me know if your thoughts are different.
Loop on the even number of time, getchar() is not taking input from keyboard, but it is taken from the previous entered hit, so you would have also noticed that loop is only executed 5 times. So you have to clear the buffer, i.e. pressed entered, so that new character can be entered in the ch.
for (i = 0; i < 5; i++)
{
printf("\nEnter a single character >> "); // new line
ch = getchar();
while (getchar() != '\n'); //Clearung buffer.
putchar(ch);
}
Not sure if ideal, but this worked:
#include <stdio.h>
int main()
{
int i;
int ch;
for(i = 0; i < 10; i++)
{
printf("Enter a single character >> ");
getchar();
ch=getchar();
putchar(ch);
}
return 0;
}
I am a beginner . I tried this version of code and it gave the desired output.
#include <stdio.h>
int main()
{
int i;
int ch;
for(i = 0; i < 10; i++)
{
printf("Enter a single character >> ");
fflush(stdin);
ch = getchar();
putchar(ch);
printf("\n");
}
return 0;
}
Although getchar() gets a single character, control isn’t returned to your program until the user presses Enter. The getchar() function actually instructs C to accept input into a buffer, which is a memory area reserved for input. The buffer isn’t released until the user presses Enter, and then the buffer’s contents are released a character at a time. This means two things. One, the user can press the Backspace key to correct bad character input, as long as he or she hasn’t pressed Enter. Two, the Enter keypress is left on the input buffer if you don’t get rid of it.to Get rid of the Enter keypress insert an extra getchar() that captures the Enter but doesn’t do anything with it.so you just need an extra getchar() after ch = getchar();like this
#include <stdio.h>
#include <stdlib.h>
int main()
{ int i;
int ch;
for(i = 0; i < 10; i++)
{
printf("Enter a single character >> ");
ch = getchar();
getchar();
putchar(ch);
}
return 0;
}
I managed to get the desired result. I added a newline character and getchar() which picks up the extra character.
#include <stdio.h>
int main()
{
int i;
char ch;
for (i = 0; i < 10; i++)
{
printf("Enter a single character >> ");
ch = getchar();
putchar(ch);
putchar('\n');
getchar();
}
return 0;
}

C while loop - code won't work

I've been writing a simple program to check if input letter is a vowel, and my code doesn't work.
The program should take characters as input one by one until % is entered, which will make it exit. It checks if input chars are vowels, and prints the result. Also it reports an error if input is not a letter.
The problem is, it breaks out of the loop on the second step.
Thank you for help, in advance.
PS Sorry, didn't write that there's no error message, it just breaks out of the loop.
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
int main(void)
{
char processed='q';
while(processed != '%')
{
printf("Enter letter to check if it's a vowel, %% to quit.\n");
char input = getchar();
processed = tolower(input);
printf("%c\n", processed);
if (processed == '%')
break;
if (processed < 'a' || processed > 'z')
{
fprintf(stderr, "Input should be a letter\n");
exit(1);
}
switch(processed)
{
case 'a':
case 'e':
case 'i':
case 'o':
case 'u':
case 'y':
printf ("Vowel\n");
break;
default:
printf ("Non-vowel\n");
}
}
exit(0);
}
Presumably you're entering a character and then hitting [ENTER]. So, in actuality you are entering two characters -- the letter you typed and a line feed (\n). The second time through the loop you get the line feed and find that it's not a letter, so you hit the error case. Perhaps you want to add something like:
if (processed == '\n') {
continue;
}
Someone else mentioned that you're hitting enter after each letter of input, and thus sending a newline ('\n') into your program. Since your program doesn't have a case to handle that, it isn't working right.
You could add code to handle the newline, but using scanf would be easier. Specifically, if you replaced
char indent = getchar();
with
char indent;
scanf("%c\n", &indent);
scanf() would handle the newline and just return back the letters you're interested in.
And you should check scanf()'s return value for errors, of course.

Resources