K&R: Chapter 6 - Why getword() function does not read EOF? - c

This is my very first post on Stack Overflow, so I hope I don't step on anyone's toes.
Of course, all inputs are welcome and appreciated, but those most suited to answer would have actually read the book, C Programming Language, 2nd ed.
I have just finished coding Exercise 6-4, but I cannot seem to figure something out. Why does the getword() function not read EOF until I press Ctrl+D (I code in C in an Arch Linux VM)?
Many of my previous exercises from the book require reading from stdin. One way I would do it is via something like
while ((c = getchar()) != EOF) {...}
In such an instance, I never have to press Ctrl+D. I enter in my input, press Enter, the stdin buffer gets flushed out, and EOF is detected automatically. The getword() function also relies on getchar() at its base, so why does it hang my program?
The getword() function is called from main():
while (getword(word, MAX_WORD) != EOF) {
if (isalpha(word[0])) {
root = addtree(root, word);
}
}
The getword() function itself:
int getword(char *word, int lim) {
char *w = word;
int c;
while (isspace(c = getch())) {
}
if (c != EOF) {
*w++ = c;
}
// This point is reached
if (!isalpha(c)) {
// This point is never reached before Ctrl+D
*w = '\0';
return c;
}
for ( ; --lim > 0; w++) {
if (!isalnum(*w = getch())) {
ungetch(*w);
break;
}
}
*w = '\0';
return word[0];
}
I put comments to indicate the point where I determined that EOF is not being read.
The getch() and ungetch() functions are the same ones used in the Polish notation calculator from Chapter 4 (and that program was able to read EOF automatically - by pressing Enter):
#define BUF_SIZE 100
char buf[BUF_SIZE];
int bufp = 0;
int getch(void) {
return (bufp > 0) ? buf[--bufp] : getchar();
}
void ungetch(int c) {
if (bufp >= BUF_SIZE) {
printf("ungetch: too many characters\n");
}
else {
buf[bufp++] = c;
}
}
Thus far, this is the first program I wrote since the beginning of this book that requires me to manually enter the EOF via Ctrl+D. I just can't seem to figure out why.
Much appreciation in advance for explanations...

Having to type Ctrl+D to get EOF is the normal behavior for Unix-like systems.
For your code snippet:
while ((c = getchar()) != EOF) {...}
pressing Enter definitely shouldn't terminate the loop (unless your tty settings are badly messed up).
Try compiling and running this program:
#include <stdio.h>
int main( void )
{
int c;
while ((c = getchar()) != EOF) {
putchar(c);
}
return 0;
}
It should print everything you type, and it should terminate only when you type control-D at the beginning of a line (or when you kill it with control-C).

The 'not reached' point would only be reached if you did something like type a punctuation mark in the input - or you read EOF. If you type a letter, or spaces, then it is bypassed.
When input is coming from a terminal (standard input), then EOF is not detected until you type Control-D (or whatever is specified in the stty -a output) after you enter a newline, or after you hit another Control-D (so two in a row). The code reads through newlines because the newline character '\n' satisfies isspace().

The source of my confusion vis-a-vis my previous programs was that the effect of my previous programs were always printed to stdout inside the while loop, so I always immediately saw the result without needing to feed in EOF. For this one, the tree is not printed until after the while loop ends, so the EOF encounter was needed. I failed to recognize that, and that's why I was going insane.
Thanks again for setting me straight!

Related

getchar() stuck in a loop never reaching the EOF

#include <stdio.h>
int main() {
int c;
while(getchar() != EOF) {
if (getchar() == ' ') {
c++;
}
printf("%i", c);
}
}
I realized that typing in a sentence like the one you're reading right
I\nrealized\nthat\ntyping\nin\n\a\n ...
i believe that's how it's being read, getchar() does not reach the EOF to make the condition in the while parentheses false..
my goal here is to make a program that takes in input from me..
reads it
if there are any spaces
it counts on a counter
when EOF is reached
the condition to keep reading it becomes false
the counter value gets printed out on the screen
to show me how many spaces i had in my entire input..
is it impossible? is that why people just use scanf() ?
this is the output i get when trying something
user#user:/c# ./a.out
hello stackoverflow this does not do what i want it to
001111111222223344445666677
You need to put the result of getchar() into a variable:
int ch;
while ((ch = getchar()) != EOF)
You shouldn't call getchar() a second time to check if it's a space, since that will read a second character so you'll be testing every other character, just compare the variable:
if (ch == ' ')
And if you want to see the total number of spaces, put the printf() at the end of the loop, not inside it.
So the whole thing should look like:
#include <stdio.h>
int main() {
int counter=0;
int ch;
while((ch = getchar()) != EOF) {
if (ch == ' ') {
counter++;
}
}
printf("%i\n", counter);
}
To send EOF from the terminal, type Control-d on Unix, Control-z on Windows.

Why won't printf return # of spaces text?

Not sure why the following code does not return the number of spaces when providing a sentence for getchar(). I want the program to return one line of text that records the number of spaces in a given sentence.
#include <stdio.h>
main()
{
int blarp, space;
space = 0;
printf("give me input\n");
while((blarp = getchar()) != EOF)
{
if(blarp == ' ')
{
space++;
}
}
printf("there are %d spaces", space);
}
As OP wants to operate on a line of data "...to return one line of text ...", detect an end-of-line '\n' as well as EOF.
while((blarp = getchar()) != EOF)
while((blarp = getchar()) != EOF && blarp != '\n')
The problem is with the EOF you used in the stopping condition of while it means End Of File and is used when reading from a file. As notified by Pooya it can work by giving EOF as input from the keyboard as:
Actually you can send EOF as input. if you are working with Windows hit Ctrl+Z and in Linux Ctrl+D. It is treated as EOF – Pooya
But, as notified by William Pursell, that would be a workaround rather than a valid input:
Pooya, no, you can't "send EOF". You can close the input stream, which will cause getchar to return EOF. ctrl-D just closes the input stream. Perhaps this is semantics, but "send EOF" implies a sentinel character, which does not exist. – William Pursell
But, to avoid the complication, you will have to use some sentinel value to stop the while loop, I have used '!' here but you can use any suitable character:
#include <stdio.h>
main()
{
int blarp, space;
space = 0;
printf("give me input\n");
while((blarp = getchar()) != '!')
{
if(blarp == ' ')
{
space++;
}
}
printf("there are %d spaces\n", space);
}
You will have to enter '!', the sentinel value, at the end of your input to stop the while loop.
Here is the snapshot of my output:

Kernighan & Ritchie code example confusion

Is there any reason of the second 'c = getchar()' mention in this code example?
#include <stdio.h>
/* copy input to output; 1st version */
int main(void) {
int c;
c = getchar();
while (c != EOF) {
putchar(c);
c = getchar(); // <-- I mean this one.
}
return 0;
}
c = getchar(); //read for the first time before entering while loop
while (c != EOF) {
putchar(c);
c = getchar(); // read the next input and go back to condition checking
}
return 0;
first getchar() reads the first time input character.
second getchar() keeps on reading next input(s), untill a EOF
In other words, the purpose of while (c != EOF) is to keep on checking whether the c is EOF or not. if c is not changed, then the while() loop is meaningless, isn't it? The second getch() is responsible for chnaging the value of c in each iteration.
yes, so it wont putchar EOF.
It reads the first character, checks that its not EOF, then putChars it, then gets another char, back to the top of the while loop and checks its not EOF.
The second c = getchar() is to read another char and yet anther one until EOF is met.
first c = getchar(); will only work Once, but c = getchar(); inside while loop will work every time until c != EOF.
c = getchar(); // Read value of `c` if `c != EOF` it will enter while loop else it will exit
while (c != EOF) { // checking condition
putchar(c); //printing value of c
c = getchar(); // again getting new value of c and checking in while loop,
//if condition is true it will continue, else it will exit
}
It's there because a while loop tests at the top but you really need the test in the middle. An alternative to duplicating code above the loop and inside it, is using break.
while (1) {
c = getchar();
if (c == EOF) break; /* test in middle */
putchar(c);
}
That's my inattention. I was running in terminal this version of code:
while((c = getchar()), c != EOF) {
putchar(c);
}
and couldn't see the difference between results. Stupid situation.
Thanks to all anyway.

Checking whether the input is a whole number

I need to make a function that gets input from the user and makes sure that it is a whole number and does not contain any character.
I wrote this code which works perfectly for integers and for single characters. But it terminates if I enter dfd i.e. multiple character input. Below is my code compiled with gcc on Linux:
#include <ctype.h>
int getint()
{
int input = 0;
int a;
int b, i = 0;
while((a = getchar()) != '\n')
{
if (a<'0'||a>'9')
{
printf("\nError in input!Please try entering a whole number again:");
input=0;
fflush(stdin);
return getint();
}
b = a - '0';
input = ((input*10) + b);
i++;
}
return input;
}
Calling fflush on an input stream invokes undefined behaviour. Even if your implementation defines it for input streams, it's not portable. There is no standard way to flush an input stream. Therefore, the fflush(stdin); is not correct. You should read the characters and discard them till and including the newline in the stdin buffer. I suggest the following change to your function.
int getint(void) {
int input = 0;
int a;
while((a = getchar()) != '\n') {
if (a < '0' || a > '9') {
printf("Error in input!Please try entering a whole number again:\n");
input = 0;
// read and discard characters in the stdin buffer up till
// and including the newline
while((a = getchar()) != '\n'); // the null statement
return getint(); // recursive call
}
input = (input * 10) + (a - '0');
}
return input;
}
Also, please read this C FAQ - If fflush won't work, what can I use to flush input?
The problem may be that calling fflush(stdin) is undefined. fflush is for flushing an output stream, not an input stream. Try replacing it with another way to clear the remaining input buffer, like while (getchar() != '\n'); and see if that resolves the issue. (you should probably do something more robust like also catching EOF so you're not in an infinite loop)
Changing the fflush to fpurge caused your program to start working for me.

echoing of getchar and '\n' char from stdin

I have already looked at this similar question but i am still wondering if there is another way to stop 1) the terminal echoing with portabilty as this is an assignment and I have already had one java program crash and burn on my teachers computer 2) in my program i search for a '\n' char then if it isn't the first char use getchar then putchar till the next '\n' char which works fine when using redirected stdin but when I try using the program without redirection the enter key is always echoed, is this to do with the terminal echoing or do i need to check for a diffrent char apart from '\n'? I have also tried including '/r' and done lots of googling but it seems the answer to the echo is can't be done with portabilty?
#include <stdio.h>
#include <string.h>
int first_line(char);
int main(){
char c;
while((c = getchar())!=EOF){
first_line(c);
}
return 0;
}
int first_line(char c){
if (c != '\n'||c != '\r'){
putchar(c);
do{
c = getchar();
putchar(c);}
while( c !='\n');
}
return 0;
}
Thanks Lachlan
For a start try with the following :
1) the condition should be if (c != '\n' && c != '\r')
2) and the while loop ,in case if terminal is line buffered then you are better of using getchfrom ncurses library the library packages should be there for most platforms.
while((c =getchar())!='\n') {
putchar(c);
}

Resources