[comp.lang.c] single character input

braun@drivax.UUCP (Kral) (05/20/88)

Before everyone thinks I'm a total blockhead...

Someone, whose id I was clever enough to delete, wrote in response to my
original question:

: 
: Quickest way is to use gets() to grab a whole line of input at once.
: You only care what the first character is, right?  So only look at that
: and ignore the rest.
: 

And this is what I ended up doing.  It *is* rather obvious, isn't it?  I just
drew a total blank, mostly because of what I consider to be rather odd
behaviour on the part of scanf.  For instance, if I do the following:

	char	c ;


	puts(prompt) ;
	scanf("%c ", c) ;
	switch(c)
	{
	:
	}

The program ends up reading one line behind what is input; in other words, I
have to enter something (anything!) the first time, then the second line
entered triggers the program reading the first line, etc.  Not at all what I
expected, and it seems not too useful to me.  I got so hung up on scanf that
I couldn't think of anything else.  I wonder why scanf does this?  Am I missing
something equally obvious here?

So the question was really a scanf problem (although I really thought there
would have been an existing library routine to do what I wanted).

So, nevermind.


-- 
kral 	408/647-6112			...{ism780|amdahl}!drivax!braun
"I'll let you be in my dream                      If I can be in yours"
DISCLAIMER: If DRI knew I was saying this stuff, they would shut me d~-~oxx

peter@athena.mit.edu (Peter J Desnoyers) (05/20/88)

In article <3423@drivax.UUCP> braun@drivax.UUCP (Kral) writes:
>... what I consider to be rather odd
>behaviour on the part of scanf.  For instance, if I do the following:
>	char	c ;
>	puts(prompt) ;
>	scanf("%c ", c) ;
>	switch(c) {
>	 ... }
>The program ends up reading one line behind what is input; in other words, I
>have to enter something (anything!) the first time, then the second line
>entered triggers the program reading the first line, etc.  Not at all what I
>expected, and it seems not too useful to me.  I got so hung up on scanf that
>I couldn't think of anything else.  I wonder why scanf does this?  Am I missing
>something equally obvious here?

Someone here made the mistake of changing one of the intro programming
courses at MIT from pascal (I think) to C, and at least one person I know
got caught by this same bug. They wrote:

  for (...) { printf( "y/n:"); scanf( "%c", &answer); ...}

Unfortunately, what they got was:
    value read:		input typed:
       'y'		    'y\n'	(note - scanf will not return until
       '\n'		    nothing      you hit return)

and so forth. Since they were testing only against 'y', they never noticed
that the non-y value wasn't a 'n', and got seriously confused.

Moral - the interaction of character-at-a-time reads through scanf or
whatever and buffered IO in the TTY driver is non-intuitive. Remember
that you are going to get every character typed at the program, but a
line at a time. 

				Peter Desnoyers
				peter@athena.mit.edu

Please, no flames about C not being appropriate to an intro programming
course. (It was 1.00 or 2.10 - taught by either civ. e. or mech. e.)
You don't have a good idea of how the computer (not the language, the
computer) works until the end of a course - C requires this knowledge
in the beginning. Besides, if you know {C, Pascal} you know {Pascal, C}.
They're all Algol derivatives, anyway.

braun@drivax.UUCP (Kral) (05/26/88)

Allow me to reply to several articles at once.

Several people started out their postings with "Perhaps I missed something...".
I think what you missed was that I had started out with scanf() to get the
single character input.  I got the type of interaction described by
peter@athena.mit.edu (Peter J Desnoyers) in <5455@bloom-beacon.MIT.EDU>:

:...and at least one person I know
:got caught by this same bug. They wrote:
:
:  for (...) { printf( "y/n:"); scanf( "%c", &answer); ...}
:
:Unfortunately, what they got was:
:    value read:		input typed:
:       'y'		    'y\n'	(note - scanf will not return until
:       '\n'		    nothing      you hit return)
:
:and so forth. Since they were testing only against 'y', they never noticed
:that the non-y value wasn't a 'n', and got seriously confused.
:

Also note: the whole idea of using scanf() in the first place was so that I
could use a previously written library routine to read the line (the system
handles the line editing (backspace, etc)) scan it for the correct pattern
(in this case, a single character input) and return an error if anything (and
I mean *anything*, like multi-character input, etc) else was entered.

Also, davidsen@steinmetz.ge.com (William E. Davidsen Jr), in Article
<10900@steinmetz.ge.com>, says:
:
:Perhaps I misunderstand your problem. It sounds as if you want to read
:the first character on a line and discard the rest.
:
:	int read1() {
:	  register int ch1, ch2;
:	  
:	  ch1 = ch2 = getchar();
:	  while (ch2 != '\n') ch2 = getchar();
:	  return(ch1);
:	}

Correct.  And, in fact, I did this for a while, before discarding it in favor
of using gets() instead.  It does the same thing.  In either case, you don't
get a return from the function until after the newline has been read by the
system.  This eliminates the need for me to have to write code to handle
backspaces, and suggested by dheeraj@sdag.cs.umd.edu (Dheeraj Sanghi) in
Article <11586@mimsy.UUCP>.

In Article <6151@sigi.Colorado.EDU>, swarbric@tramp.Colorado.EDU (Frank 
Swarbrick), says:
:
:I must have missed something along the line somewhere.  ch = getchar() works
:fine for me.  I can use backspace to edit it.  I can type as many characters as
:I want.  When I finally press return the first character that is left is
:assigned to ch.
:
:Is this not the way getchar() works on some machines/compilers?
:

The problem is this: if the first character input is not a valid input, what do
you do?  You loop back, redisplay the prompt (probably with some sort of error
message along the way) and read again.  What you will get is the next character
input on the old line, which will probably also be wrong, and the process will
continue until all of the character have been read.  The result is a bunch of
error messages displayed because the user entered one bad input line.

I guess the proper thing to do, then, is either write my own function (which I
probably will do), or use gets and sscanf().

Thanx for all of your comments, both posted and emailed.


-- 
kral 	408/647-6112			...{ism780|amdahl}!drivax!braun
"I'll let you be in my dream                      If I can be in yours"
DISCLAIMER: If DRI knew I was saying this stuff, they would shut me d~-~oxx

daveb@laidbak.UUCP (Dave Burton) (05/26/88)

In article <3443@drivax.UUCP> braun@drivax.UUCP (Kral) writes:
|In Article <6151@sigi.Colorado.EDU>, swarbric@tramp.Colorado.EDU (Frank 
|Swarbrick), says:
|:I must have missed something along the line somewhere.  ch = getchar() works
|:fine for me.  I can use backspace to edit it.  I can type as many characters as
|:I want.  When I finally press return the first character that is left is
|:assigned to ch.
|The problem is this: if the first character input is not a valid input, what do
|you do?  You loop back, redisplay the prompt (probably with some sort of error
|message along the way) and read again.  What you will get is the next character
|input on the old line, which will probably also be wrong, and the process will
|continue until all of the character have been read.  The result is a bunch of
|error messages displayed because the user entered one bad input line.

Just flush the rest of the line with a gets().
If you really need punctual response, and control over how many characters
the user can type before [RETURN], you'll need to use a so-called raw mode,
(or at least set CBREAK if using UNIX).

#include <stdio.h>
main()
{
	int c;
	char buf[80];

	printf("yes or no: ");
	while ((c = getchar()) != 'y' && c != EOF) {
		gets(buf);
		printf("try again.\n");
		printf("yes or no: ");
	}
	printf("done.\n");
}

chris@mimsy.UUCP (Chris Torek) (05/26/88)

In article <3443@drivax.UUCP> braun@drivax.UUCP (Kral) writes:
>gets() ... does the same thing.  In either case, you don't get a return
>from the function until after the newline has been read by the system.
>This eliminates the need for me to have to write code to handle
>backspaces ....

It is worth pointing out that, under modern operating systems, that
need does not exist.  Indeed, you must take special action to get
the backspaces, if you want to handle them yourself:

  Unless you are using a primitive system, backspace editing is not
  done within gets() itself; rather, it is done at a lower level (by
  the system).

To forestall further questions here, disabling and enabling this editing
is a function of the operating system, not the language, so there is no
single answer to `how do I do it in C'.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris