Why does my simple while loop printf 2x?


B

Ben Bacarisse

BartC said:
I'm not teaching anything. I suggested to the OP that getch() might be
another approach to his/her task, and to search for a Linux version if
necessary to try it out.

Sorry, I meant a generic you not you in particular. I was merely
commenting on how this could be a more useful resource.

<snip>
 
Ad

Advertisements

K

Keith Thompson

BartC said:
void skiptoeol(void){
while (getchar()!='\n') {}
}

That's an infinite loop if you get an end-of-file condition on
standard input before seeing a '\n' (on Unix-like systems, that
can happen if the user types Ctrl-D twice, or if you're reading
from a text file that doesn't end with a '\n' character).

A fix (not tested)

void skiptoeol(void) {
int c;
while ((c = gethchar()) != EOF && c != '\n') { }
}

but this has the disadvantage that the caller can't tell whether it
returned because it saw '\n' or because it ran out of input.
 
B

Ben Bacarisse

JohnNapier said:
The following code might suit your needs better.

char c;

printf( "Enter Y or N: " );
while( 1 )
{
c = toupper( getchar() );
if( c == 'Y' || c == 'N' )
break;
else
printf( "\n%c is not a valid entry, try again: ", c );
}

I don't think that helps much. The OP's problem was line buffered
input, and this version will behave in exactly the same way.

But this made me look again and I am not sure anyone has cautioned about
using char for c here. It's not so much a problem in this code (though
printing EOF, converted to a char, might surprise the user), but it's a
bug in waiting if someone ever adds code that tests for EOF. That's not
just a theoretical possibility since one of the solutions to the OP's
problem is to read any remaining characters on the input line, and that
loop might well test for EOF. It's almost always better just to use int
for single character input. It's become almost a habit for me.
 
D

DFS

The following code might suit your needs better.

char c;

printf( "Enter Y or N: " );
while( 1 )
{
c = toupper( getchar() );
if( c == 'Y' || c == 'N' )
break;
else
printf( "\n%c is not a valid entry, try again: ", c );
}

--JohnNapier--
http://compgroups.net/comp.lang.c/


Thanks, but it's similar to my original - also doesn't work right.

X is not a valid entry, try again:

is not a valid entry, try again:



As the other guys here showed me yesterday, getchar() reads in the
character you input, plus the end of line. That's why the 1st character
in the second line is blank.

Hard to believe this is so difficult in C.
 
B

Barry Schwarz

Thanks, but it's similar to my original - also doesn't work right.

X is not a valid entry, try again:

is not a valid entry, try again:



As the other guys here showed me yesterday, getchar() reads in the
character you input, plus the end of line. That's why the 1st character
in the second line is blank.

No, that is why the lines are doubled spaced. The blank in the first
character is the blank in your format string between the "%c" and the
"is".
Hard to believe this is so difficult in C.

It's not difficult at all. You have simply fixated on an approach
that doesn't work. If you used fgets with a suitably large buffer and
examined only the first character, you would get the desired effect.
Other equally simple solutions that will also do what you want have
already been posted.
 
S

Seungbeom Kim

It's not difficult at all. You have simply fixated on an approach
that doesn't work. If you used fgets with a suitably large buffer and
examined only the first character, you would get the desired effect.

But how large should the buffer be?
The larger it is, the more memory you waste (though it may not matter
that much here). The smaller, the more likely it is that you'll get
the original problem of multiple prompts between newlines, unless you
carefully write a loop to consume the remnants of the line.

It is easy to write a "sloppy" version which will most likely work for
this type of non-critical interactive programs, but it is not trivial
to write a perfectly correct version.
Other equally simple solutions that will also do what you want have
already been posted.

Which ones?

BartC's version that had 'skiptoeol' was not so simple (for the simple
nature of the task) and contained a bug. JohnNapier's version still had
the original problem. I don't see any others.

I have to admit that this kind of problems is so difficult in C that
it keeps troubling so many beginners and even many intermediates cannot
write a correct version on their first try.
 
Ad

Advertisements

B

BartC

Seungbeom Kim said:
But how large should the buffer be?
The larger it is, the more memory you waste (though it may not matter
that much here). The smaller, the more likely it is that you'll get
the original problem of multiple prompts between newlines, unless you
carefully write a loop to consume the remnants of the line.

It is easy to write a "sloppy" version which will most likely work for
this type of non-critical interactive programs, but it is not trivial
to write a perfectly correct version.

It /is/ harder than it looks at first.

If line-oriented input is to be used (and that fits in better if the rest of
the application is command-line based), then the steps are these:

* Read an entire line into a buffer.

* Detect and return the two Y/y or N/n possibilities in the buffer (and
possibly also Yes and No), and reject every other kind of input

* Repeat until Y or N is seen.

This seems simple enough, although if you're writing this from scratch, you
will quickly see you might be doing this a lot and should be building
functions for more general use. For just starting however, throwing together
anything that will work will do.

However, C likes to throw some obstacles in the way. For example, in this
newsgroup, it is impossible to talk about any code to do with line input
(especially from a console or terminal), without it having to deal sensibly
with:

* Having input redirected from a file
* Files potentially containing lines too long for any reasonable-sized
buffer
* A possible huge, multi-GByte file
* Dealing a binary file that doesn't contain any line or text data
* All of the above.

(I've looked at how easy it might be to code the OP's task in a couple of
other languages I use. In a dynamic language, it was easy. In another, more
C-like one, it was still straightforward (but that language had line input
built-in; example is here: http://pastebin.com/CAv1P9yn)

So they both work fine when used as expected (interactive console). But
neither of them cope fully when given some completely random binary file.

But it doesn't matter! The program is being used incorrectly so it is
expected to go wrong.

The main issue is that when reaching an EOF, it will keep reading empty
lines. I might make a concession for that and return 'N' on an empty line,
or apply some limit to the number of empty (or error) lines tolerated (not
shown in my link).

Another approach is to detect such attempts to read past EOF internally and
raise an error (or simply close the file and switch to interactive mode).
But I don't think explicit EOF-checking is appropriate in user-level code
designed for interactive input.)
 
B

Ben Bacarisse

DFS said:
As the other guys here showed me yesterday, getchar() reads in the
character you input, plus the end of line.

That's not quite true. getchar reads only one character, but your
program does not see it until the newline is typed. Once the newline is
typed, your program will be bound to see all the other characters that
were typed, one after the other.
That's why the 1st
character in the second line is blank.

Hard to believe this is so difficult in C.

It's not related to C; it's an OS issue. The code BartC linked to that
(very nearly) duplicates MS's getch and kbhit does very little, but what
it does is highly technical, so it gives the feeling that this is
difficult when the fact is it is just "unnatural" on the system you are
using.

The Unix/linux tradition is to encourage people to write programs that
process input streams -- ones that don't even know there is a keyboard.
For example, there is a standard Unix program called "yes" that outputs
'y' on a line by itself again and again. It's purpose is to allow you
to automate a program that asks for confirmation by gluing the input of
the program to the output of "yes". A program that uses getch/kbhit
could not be automated in this way.

So, in short, it seems hard because you've come across a culture clash
about how programs can interact with users. It's a shame that so many
tutorials present code that is so system-specific when there are lots of
simple C programs that could be written that are system-neutral.
 
D

DFS

The following code does something along those lines. But even that is not
ideal, because someone can type anything that happens to start with Y or
N, and it will apparently work. And it won't accept a space before Y or N.

Getting this stuff to work properly is always fiddly. A solution using
getch()
would be simpler. But using line-oriented input, you should really be
using line-processing routines, where you can read a whole word, so that
you can accept 'y', 'n', 'no', 'yes' and reject anything else.

void skiptoeol(void){
while (getchar()!='\n') {}
}

int confirm(void) {
char c,d;
while (1) {
printf("Enter Y or N (not case-sensitive): ");
c=toupper(getchar());
if (c == 'Y' || c == 'N') {
skiptoeol();
break;
}
printf("Error.\n");
skiptoeol();
}
return c;
}

int main(void) {
char c;

c=confirm();
printf("%s\n",(c=='Y'?"Yes":"No"));

}


I cleaned it up a bit and shortened it by getting rid of skiptoeol() and
removing toupper() (which required a function declaration or I got a
compiler warning), but your example worked!

Thanks!



int confirm(void)
{
char c;
while (1) {
printf("Enter Y or N (not case-sensitive):");
c=getchar();
if (c=='Y'||c=='y'||c=='N'||c=='n') {
while (getchar()!='\n') {}
break;
}
while (getchar()!='\n') {}
}
return c;
}


int main(void)
{
printf("You entered: %c\n", confirm());
return 0;
}
 
K

Keith Thompson

DFS said:
Thanks, but it's similar to my original - also doesn't work right.

X is not a valid entry, try again:

is not a valid entry, try again:

As the other guys here showed me yesterday, getchar() reads in the
character you input, plus the end of line. That's why the 1st character
in the second line is blank.

Hard to believe this is so difficult in C.

You want to read the first character on an input line, and discard the
rest of the line.

Here's a function that does that:

/*
* Returns the first character on an input line;
* discards the rest of the line.
* Returns '\n' for an empty line, EOF at end-of-file.
*/
int get_first_char(void) {
int result = getchar();
if (result != EOF && result != '\n') {
int discard;
while ((discard = getchar()) != '\n' && discard != EOF) {
/* nothing */
}
}
return result;
}
 
K

Keith Thompson

BartC said:
If line-oriented input is to be used (and that fits in better if the rest of
the application is command-line based), then the steps are these:

* Read an entire line into a buffer.

Why?

If the intent is to check whether the first character on the line is
'Y', 'N', or something else, and we don't care what follows that first
character, there's no reason to store the remaining characters in a
buffer.

It's tempting to use fgets() because it's a predefined way to read a
line at a time, but of course that makes things more complex if input
lines can be arbitrarily long (and you should usually assume that they
can be). But if you write your own input loop instead, you don't need
to care how long the line is because you never need to store all of it;
just keep reading and discarding characters until you see EOF or '\n'.
 
Ad

Advertisements

K

Keith Thompson

DFS said:
I cleaned it up a bit and shortened it by getting rid of skiptoeol() and
removing toupper() (which required a function declaration or I got a
compiler warning), but your example worked!
[...]

toupper() requires "#include <ctype.h>", just as printf requires
"#include <stdio.h>".

All standard library functions are declared in standard headers, and the
documentation for each will tell you which header you need to include.

In some cases you may be able to get away with omitting the #include
directive (because earlier versions of the language were lax about such
things, and some modern compilers still support old-style code), but
you should *always* have the required #include for each library function
you call.

(It's also legal to write the declaration yourself, but you have to get
it *exactly* right to avoid undefined behavior, and the compiler likely
won't warn you if you get the declaration wrong. There's no good reason
to do that rather than just #include'ing the proper header.)
 
B

BartC

Keith Thompson said:

Because we're doing line-oriented input.

You /can/ program at a character-level, but it would be like a syntax parser
looking at its input character-by-character instead of tokenising first,
then examining whole tokens.
If the intent is to check whether the first character on the line is
'Y', 'N', or something else, and we don't care what follows that first
character, there's no reason to store the remaining characters in a
buffer.

You can at least consider the possibility of looking at more than one
character. Because it /is/ line-oriented, it means someone can enter Yoghurt
for yes, and Nigella for no, which is probably a bigger sin than
not checking for EOF on a supposedly interactive terminal.
It's tempting to use fgets() because it's a predefined way to read a
line at a time, but of course that makes things more complex if input
lines can be arbitrarily long (and you should usually assume that they
can be).

I've been using fixed-length buffers for line-oriented text-files for
decades. If it doesn't work because of a line-length problem, then the file
isn't what I would call line-oriented. (There's a simple test: can you edit
the file without (a) having to scroll a hundred screens horizontally to see
the end of any line; and (b) can you see the line without it wrapping over a
hundred lines vertically. If not, then it isn't a line-oriented text file as
is normally understood, and needs to be processed differently; as a
character-oriented one, for example.)

As for being able to read any conceivable input, that's an unnecessary
complication. Either the file is simply and practically line-oriented, or
it's not, so it's an error.
But if you write your own input loop instead, you don't need
to care how long the line is because you never need to store all of it;
just keep reading and discarding characters until you see EOF or '\n'.

Something doesn't sound right about asking the user to type Y or N (perhaps
in answer to something critical such as formatting a hard drive), and then
being tolerant about lines thousands of characters long, that just happen to
start with a Y!
 
I

Ian Collins

DFS said:
I agree! My next attempt is to limit the input to 1 character. Y or N
(or y or n). Anything else they type won't show onscreen, and they
won't be prompted again. The program will just sit there, waiting for
the right keystroke.

Such a thing is surely possible with C. When you login at a Linux
command prompt or enter your password for sudo, the password line is
'disabled' and doesn't echo anything back. I bet there's a way to
restrict it to showing only Y/N.

There is, but it isn't a "standard" C facility. Look up the curses library.
 
B

Ben Bacarisse

BartC said:
Because we're doing line-oriented input.

You /can/ program at a character-level, but it would be like a syntax parser
looking at its input character-by-character instead of tokenising first,
then examining whole tokens.

That's not good advice in this context. The fact that newlines are
significant is not enough on it's own to switch to a line buffer.

Something doesn't sound right about asking the user to type Y or N (perhaps
in answer to something critical such as formatting a hard drive), and then
being tolerant about lines thousands of characters long, that just happen to
start with a Y!

First, it's clear that in any serious application, you would expect the
test to be more thorough. You might reject any input that was not a
case-insensitive prefix of "yes" or "no", immediately followed by a
newline. Or you might limit the valid replies to "Y\n" and "N\n", but
in any case I'd accept the full line, because that is both simple and
logical, no matter how long it is. There's just no reason to try to put
it all into a buffer.

If you try to read a line into a fixed-size buffer, you absolutely must
do something special if the line is over-long. You can't risk leaving a
valid response to the next "format /home?" prompt in the street. The
upshot is that character-based input is simpler an natural in this
context.
 
B

BartC

Ben Bacarisse said:
BartC said:
Keith Thompson said:
[...]
If line-oriented input is to be used (and that fits in better if the
rest
of
the application is command-line based), then the steps are these:

* Read an entire line into a buffer.

Why?

Because we're doing line-oriented input.

You /can/ program at a character-level, but it would be like a syntax
parser
looking at its input character-by-character instead of tokenising first,
then examining whole tokens.

That's not good advice in this context. The fact that newlines are
significant is not enough on it's own to switch to a line buffer.

Line buffers can make life easier. (Read the whole line, then you can read
individual items within the buffer and stop at the end, without
inadvertently reading part of the next line and getting everything out of
sync.) If you are creating a command-line-type interface, you want to be
able to work with the whole line, and not via a one-character window.

So why make a decision to work with characters, just because of some rare
situation where an input line is many thousands of characters long?
(Actually, using that logic, there would be /no/ situation that a line
buffer could ever be used.)

To me line processing and character processing are quite different. For
example, in my line-oriented text editors, if I hold down Backspace in the
middle of a line, then it will stop at the start of the line. It will not
delete half the previous line too before I've noticed! Which latter is the
behaviour of most other text-editors I've tried, including ones purporting
to be programming editors. Many some people don't understand what
'line-oriented' actually means.
 
Ad

Advertisements

L

luser droog

getch() is a Windows-specific function that behaves as you describe.

In the UNIX/Linux world, getch() is the name of a function provided by
the curses/ncurses package, and it behaves quite differently.

You could write a function that reads a single character without needing
to press Enter, but (a) its implementation wouldn't be portable to
non-POSIX systems, and (b) I would advise against calling it "getch".

(And for the current purpose, line-buffered input is probably good
enough anyway, you just have to deal with it properly.)

Here's some example code, with or without curses.
http://stackoverflow.com/a/7411735/733077
 
B

Ben Bacarisse

BartC said:
Ben Bacarisse said:
BartC said:
[...]
If line-oriented input is to be used (and that fits in better if
the rest
of
the application is command-line based), then the steps are these:

* Read an entire line into a buffer.

Why?

Because we're doing line-oriented input.

You /can/ program at a character-level, but it would be like a
syntax parser
looking at its input character-by-character instead of tokenising first,
then examining whole tokens.

That's not good advice in this context. The fact that newlines are
significant is not enough on it's own to switch to a line buffer.

Line buffers can make life easier.

Agreed. I did not say otherwise.
(Read the whole line, then you can
read individual items within the buffer and stop at the end, without
inadvertently reading part of the next line and getting everything out
of sync.) If you are creating a command-line-type interface, you want
to be able to work with the whole line, and not via a one-character
window.

It's not easy "keep everything in sync" with a fixed size line buffer
and a flexible one is significantly more code.
So why make a decision to work with characters, just because of some
rare situation where an input line is many thousands of characters
long?

Because in many cases, like this one, it's simply easier. I am not on
some crusade from the "billion character lines must be accepted" church,
I am making a pragmatic point from experience. I've tried both in
various situations, and in many -- specifically this context --
character reading is simpler. It does depend on the exact semantics that
you want, exactly what should be accepted and what rejected, but I'm
having a hard time seeing how fixed-line reading would win out.

Depending on the desired semantics, you may well need a buffer, but it
won't be a line buffer and it's size may be logically determined.
(E.g. to accept "yes", "no", "y" or "n" you might store up to four
characters, but there's no guesswork about how many you need to store).
(Actually, using that logic, there would be /no/ situation that
a line buffer could ever be used.)

To me line processing and character processing are quite
different. For example, in my line-oriented text editors, if I hold
down Backspace in the middle of a line, then it will stop at the start
of the line. It will not delete half the previous line too before I've
noticed! Which latter is the behaviour of most other text-editors I've
tried, including ones purporting to be programming editors. Many some
people don't understand what 'line-oriented' actually means.

I don't really see the connection here. You may use any editing
interface you find comfortable, but I see no consequences for how one
might write prompt-reply code.
 
D

DFS

DFS said:
I cleaned it up a bit and shortened it by getting rid of skiptoeol() and
removing toupper() (which required a function declaration or I got a
compiler warning), but your example worked!
[...]

toupper() requires "#include <ctype.h>", just as printf requires
"#include <stdio.h>".

All standard library functions are declared in standard headers, and the
documentation for each will tell you which header you need to include.

In some cases you may be able to get away with omitting the #include
directive (because earlier versions of the language were lax about such
things, and some modern compilers still support old-style code), but
you should *always* have the required #include for each library function
you call.

(It's also legal to write the declaration yourself, but you have to get
it *exactly* right to avoid undefined behavior, and the compiler likely
won't warn you if you get the declaration wrong. There's no good reason
to do that rather than just #include'ing the proper header.)

I wrote the declaration myself - it was simple.

int toupper(int c); as I recall.

No compiler warning, and the program ran fine. But as you say, an
#include would be better.


Thanks for info, Keith. I had never heard of ctype.h. I just started
learning C a couple weeks ago. Some of it is very confusing - the
string handling in particular is strange. VB is much easier, tho not as
powerful.
 
Ad

Advertisements

D

DFS

Something doesn't sound right about asking the user to type Y or N (perhaps
in answer to something critical such as formatting a hard drive), and then
being tolerant about lines thousands of characters long, that just
happen to start with a Y!


I agree! My next attempt is to limit the input to 1 character. Y or N
(or y or n). Anything else they type won't show onscreen, and they
won't be prompted again. The program will just sit there, waiting for
the right keystroke.

Such a thing is surely possible with C. When you login at a Linux
command prompt or enter your password for sudo, the password line is
'disabled' and doesn't echo anything back. I bet there's a way to
restrict it to showing only Y/N.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top