1

This code is a simplification from a larger project I'm working on and it sums up the problem in a simple example. I am obtaining input from the user, their name, and then clearing the buffer from any input that did not fit in the C-string. The problem is that After entering the name, the user has to push enter twice for the program to respond, and because I am using getchar() to flush the buffer there is just a clear misunderstanding in the logic of the loop I created. How can I keep the user from entering Enter twice, in otherword what am I missing? Thanks!

#include "stdafx.h"
#include <stdio.h>
#include <string.h>

#define BUFFSIZE 10

int main(void)
{ 

    unsigned char name[BUFFSIZE];

    printf("ENTER YOUR NAME: ");
    fgets(name, BUFFSIZE, stdin);
    name[strcspn(name, "\n")] = '\0';

    //flush the input buffer
    int flush;
    while (flush = getchar() != '\n' && flush != EOF);

    printf("Your name is: %s!\n ", name);
    printf("Press enter to continue...");
    getchar();

    return 0;
} 
chris360
  • 115
  • 5
  • 3
    Note: "flushing" commonly refers to output buffers/streams/etc. Input cannot be "flushed", but it can be dropped. – too honest for this site Sep 09 '16 at 22:10
  • Possible duplicate of [How to clear input buffer in C?](http://stackoverflow.com/questions/7898215/how-to-clear-input-buffer-in-c) For the record, the answer is use `fseek(stdin,0,SEEK_END);` – ffledgling Sep 09 '16 at 22:10
  • 2
    There's nothing to flush if `fgets` put a `'\n'` in your buffer. If the buffer is too small, then `fgets` will fill the buffer, but there won't be a `'\n'` at the end of the buffer. – user3386109 Sep 09 '16 at 22:10
  • 1
    flush is not necessary If the result will include a trailing newline to `name` . – BLUEPIXY Sep 09 '16 at 22:11
  • 3
    `fgets()` reads the newline. So if you loop until the user types a newline, the user has to type enter again. You seem to be confusing this with the case where you use `scanf()` and then want to call `fgets()` after it, because `scanf()` doesn't read the newline. – Barmar Sep 09 '16 at 22:13
  • 4
    @ffledgling `fseek()` doesn't work on the terminal. – Barmar Sep 09 '16 at 22:14
  • Standard C library support for user input is very primitive, dates back to an era when computers were very expensive and slow and had to be used by more than one user. Nothing happens until you press the Enter key, then you get everything the user typed on a single line. Not like your CRT didn't fix this, it just isn't standard. Look for something resembling "getch". – Hans Passant Sep 09 '16 at 22:18
  • @ffledgling Because it only works on devices that allow seeking, which basically means ordinary files. The terminal driver doesn't allow you to jump around in the stream. – Barmar Sep 09 '16 at 22:21
  • What if I use fgets() twice on the same c string? I don't want the input(if any) that is left in the buffer because the user enters too many chars. I use the while loop to "get rid" of the extra input stored in the buffer, the question is how to I make it so the user doesn't have to press enter twice? – chris360 Sep 09 '16 at 22:22
  • Or better yet what is this code doing to cause the second enter to be nessesary? Im just not understanding what is causing it – chris360 Sep 09 '16 at 22:27
  • The `fgets` reads the first newline. Then the `getchar` keeps reading until there's another newline. If you type a name that's longer than 10 characters, you'll find that you *don't* need to press enter twice. That's because the `fgets` won't read the first newline, and the `getchar` will read the first newline. – user3386109 Sep 09 '16 at 22:31
  • @user3386109 Thank you! So how could I make it function with out the second enter assumeing user enters input within the bounds? – chris360 Sep 09 '16 at 22:33
  • Change `#define BUFFSIZE 10` to `#define BUFFSIZE 1000` – Weather Vane Sep 09 '16 at 22:51
  • Take WeatherVane's advice and make the buffer big enough so that you never need the `getchar`. Or check the buffer after calling `fgets` to see if it contains a newline. If it does, don't call `getchar`. – user3386109 Sep 09 '16 at 22:55
  • Ok, thank you guys so much for your help! I will take weather vanes advice! – chris360 Sep 09 '16 at 23:00
  • Assignments have a lower precedence than either equality or logical operators. `flush = getchar() != '\n' && flush != EOF` is equivalent to `flush = (getchar != '\n' && flush != EOF)` – kdhp Sep 09 '16 at 23:58
  • @kdhp omgosh thats probably it right there, I really should have checked the precedence of operators as it is clear i have forgoten the real order and inputed my own – chris360 Sep 10 '16 at 00:00
  • @kdhp that works as long as the user enters more than the buffer needs! Otherwise there is no new line or EOF unless i hit enter! Ill figure it out from here it makes much more sense now thank you!!! – chris360 Sep 10 '16 at 00:05

3 Answers3

1

First, the remainder of the line should not be consumed if there is no remainder, otherwise an additional line will be skipped over. Secondly, assignment has a lower precedence than most operations, meaning that flush = is evaluated after
getchar() != '\n' && flush != EOF.

When comparing the result at assignment explicit parenthesis should be added:
flush = getchar() != '\n' to (flush = getchar()) != '\n'
Alternatively the assignment can be moved outside of the conditional, see below.

The following edit uses strlen to get the final character, and moves the assignment into the loop.

#include <stdio.h>
#include <string.h>

#define BUFFSIZE 10

int main(int argc, char *argv[])
{ 
    char name[BUFFSIZE];
    size_t len;
    int c;

    printf("ENTER YOUR NAME: ");
    fgets(name, BUFFSIZE, stdin);
    len = strlen(name);
    if (name[len - 1] == '\n')
        name[len - 1] = '\0';
    else
        do
            c = getchar();
        while (c != '\n' && c != EOF);

    printf("Your name is: %s!\n ", name);
    printf("Press enter to continue...");
    getchar();

    return 0;
} 
kdhp
  • 2,096
  • 14
  • 15
1

The problem in your program is that you don't distinguish the two cases: a) the user's input fit into the buffer versus b) the input didn't fit. What distinguishes these cases is the presence of the newline character in the buffer. That information is destroyed when you overwrite that character:

fgets(name, BUFFSIZE, stdin);
name[strcspn(name, "\n")] = '\0';

What we need here is something like:

size_t nlcspn = strcspn(name, "\n");
bool incomplete = name[nlcspn] == 0;
name[nlcspn] = 0;

Now we have an incomplete flag to test. Only when this flag informs that the input didn't contain a newline, then we can go ahead and complete the "get line" operation of fgets with a little loop that scans until a newline is received. (In that case, some error recovery might also be a good idea, like informing the user that the input had been too long, and creating an opportunity to rectify that).

Another thing to note is that fgets returns a value which should be checked. If it returns a null pointer, it means that the stream ended before any input was consumed. The problem is that in that case, fgets doesn't put anything into the array. The array retains its previous value, which may be previously read input, or an indeterminate value ("garbage") due to uninitialized contents.

Kaz
  • 55,781
  • 9
  • 100
  • 149
  • your answer is IMO the best thus far, thank you for answering! I know about the return value of fgets, this was just a quick write to display the problem, in the project im working on I do check the fgets return value! Thanks again! – chris360 Sep 10 '16 at 01:15
-2

My wild guess just reading your code is that the error is here :

while (flush = getchar() != '\n' && flush != EOF);

You want to getchar() until the output buffer is '\n' or EOF, right ? Then try this :

while (flush = getchar() != '\n' || flush != EOF);
Magix
  • 4,989
  • 7
  • 26
  • 50
  • Thank you for your answer ill test it when i get home! I hope it works, it seems like it should! – chris360 Sep 09 '16 at 23:37
  • Actually thats an infinite loop now that i think about it – chris360 Sep 09 '16 at 23:42
  • Uh, i don't see how this is an infinite loop... But didn't test it, so maybe it is – Magix Sep 09 '16 at 23:47
  • That says: continue looping as long as != '\n' OR != EOF is true! Thanks anyway for trying! – chris360 Sep 09 '16 at 23:47
  • Lol not yet but think about it, trust me, using && says both have to remain true in order to loop whereas || says either or has to be true in order to loop, flush cant be both newline and eof therefore the || will never fail. – chris360 Sep 09 '16 at 23:51
  • All right, I trust you, but i still don't get it... I'll look at it tomorrow when i'll have my brain plugged in. Anyway, try it for the sake of doing it, we never know... ;-) – Magix Sep 09 '16 at 23:53
  • Of course! Ill get back to you if it works! Thanks again! – chris360 Sep 09 '16 at 23:54
  • 1
    @chris360 is correct, the expression given always evaluates to true. From the description you meant `!((flush = getchar()) == '\n' || flush == EOF)` (`flush` is neither `\n` nor `EOF`), which by [De Morgan's](https://en.wikipedia.org/wiki/De_Morgan%27s_laws) is equivalent to `(flush = getchar()) != '\n' && flush != EOF`. – kdhp Sep 10 '16 at 00:10