Why does my simple while loop printf 2x?


D

DFS

char c;
while(1)
{
printf("Enter Y or N: ");
c = toupper(getchar());
if (c == 'Y' || c == 'N') {break;}
}
return c;


If I enter Y or y or N or n it works.

If I enter any other letter it says:

Enter Y or N: Enter Y or N:

(that's 2x) and waits for input.


Thanks for help!
 
Ad

Advertisements

B

BartC

DFS said:
char c;
while(1)
{
printf("Enter Y or N: ");
c = toupper(getchar());
if (c == 'Y' || c == 'N') {break;}
}
return c;


If I enter Y or y or N or n it works.

If I enter any other letter it says:

Enter Y or N: Enter Y or N:

(that's 2x) and waits for input.

What did you expect it to do when you enter anything other than Y or N?

You've told it to repeatedly prompt for input until someone enters Y or N!

You might add this at the end of the loop (before the }):

printf("\nInput error.\n");
 
F

Fred K

char c; while(1) { printf("Enter Y or N: "); c = toupper(getchar()); if(c == 'Y' || c == 'N') {break;} } return c; If I enter Y or y or Nor n it works. If I enter any other letter it says: Enter Y or N: Enter Y or N: (that's 2x) and waits for input. Thanks for help!

You entered two characters (some character plus a carriage return)
The first getchar() returns the character you entered; it fails your test, so continues the loop and prints the query.
then getchar() reads the carriage return. That too fails the test, and prints the query again.
 
D

DFS

What did you expect it to do when you enter anything other than Y or N?

I expect it to stay in the While loop until Y or N is entered.


You've told it to repeatedly prompt for input until someone enters Y or N!

Exactly!

But my question still stands: why does it prompt 2x every time I enter
something other than Y or N?


You might add this at the end of the loop (before the }):

printf("\nInput error.\n");

This is what happens when I do that:

Input error.
Enter Y or N:
Input error.
Enter Y or N:



Thanks for your help, BartC
 
D

DFS

You entered two characters (some character plus a carriage return)
The first getchar() returns the character you entered; it fails your test, so continues the loop and prints the query.
then getchar() reads the carriage return. That too fails the test, and prints the query again.


Gotcha. Nice explanation.


Thanks
 
B

BartC

DFS said:
On 05/15/2014 02:14 PM, BartC wrote:

Exactly!

But my question still stands: why does it prompt 2x every time I enter
something other than Y or N?

OK I misunderstood. I thought you meant the old and new prompts together
making a double prompt.

This is because you're doing line input so you end up getting two
characters. So if someone types:

A<enter>

then you will read 'A' followed by '\n'.

You just have to keep reading characters until you skipped the '\n'
(remembering someone might type a whole line of text).
 
Ad

Advertisements

B

BartC

BartC said:
OK I misunderstood. I thought you meant the old and new prompts together
making a double prompt.

This is because you're doing line input so you end up getting two
characters.

BTW if you have the getch() or _getch() function available, then you might
consider using that. (I probably assumed you were, I didn't look closely at
the char input line.)

This lets you type Y or N with a single key-press without needing to press
Enter (and you wouldn't have had that problem).

getch() should be on Windows, while there are also versions available (as a
fragment of source to download) for Linux.
 
D

DFS

OK I misunderstood. I thought you meant the old and new prompts together
making a double prompt.

This is because you're doing line input so you end up getting two
characters. So if someone types:

A<enter>

then you will read 'A' followed by '\n'.

You just have to keep reading characters until you skipped the '\n'
(remembering someone might type a whole line of text).


Thanks.

With yours and FredK's help, I changed it slightly and it almost works
perfectly:


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


No more double prompts, but letters other than y/n return the \n (shows
as blank onscreen).

So, how do I read just the nth character from getchar()?
 
B

BartC

DFS said:
On 05/15/2014 02:47 PM, BartC wrote:
char c; printf("Enter Y or N (not case-sensitive): ");
while(c != '\n')
{
c = toupper(getchar());
if (c == 'Y' || c == 'N') {break;}
}
return c;


No more double prompts, but letters other than y/n return the \n (shows as
blank onscreen).

So, how do I read just the nth character from getchar()?

It's not clear what you're trying to do.

At the moment this function returns Y or N when someone types a line
containing Y or N (so 'xyz' returns 'Y', and 'anything' returns 'N'). And it
returns \n for an empty line or one not containing Y or N (because that is
the value of c at the end of the while loop).

You need a better specification for this function.

For example, should it only ever return Y or N when the line starts with
those, and stay in the function until the user does that?
 
D

DFS

BTW if you have the getch() or _getch() function available, then you
might consider using that. (I probably assumed you were, I didn't look
closely at the char input line.)

This lets you type Y or N with a single key-press without needing to
press Enter (and you wouldn't have had that problem).

getch() should be on Windows, while there are also versions available
(as a fragment of source to download) for Linux.


I might like getch(), if I can get it to work.


I added

#include <conio.h>

but gcc threw a fatal error:
conio.h: No such file or directory


Here's the entire, simple program.

======================================================
#include <stdio.h>

int toupper(int c);
int getch(void);

char getLetter()
{
char c;
printf("Enter Y or N (not case-sensitive): ");
while(c != '\n')
{
c = toupper(getch());
if (c == 'Y' || c == 'N') {break;}
}
return c;
}


int main(int argc, char *argv[])
{
printf("You entered: %c\n", getLetter());
return 0;
}
======================================================


gcc says:
/tmp/cc6dRK3Z.o: In function `getLetter':
getLetter.c:(.text+0x29): undefined reference to `getch'
collect2: error: ld returned 1 exit status



Thanks for your help
 
D

DFS

It's not clear what you're trying to do.

At the moment this function returns Y or N when someone types a line
containing Y or N (so 'xyz' returns 'Y', and 'anything' returns 'N').

I see that now!


And it returns \n for an empty line or one not containing Y or N
(because that is the value of c at the end of the while loop).

You need a better specification for this function.

For example, should it only ever return Y or N when the line starts with
those, and stay in the function until the user does that?


Ideally, I want the user to enter Y or N (upper or lower case).
Anything else and they keep getting prompted for Y or N.
 
Ad

Advertisements

B

BartC

DFS said:
On 05/15/2014 03:08 PM, BartC wrote:


I might like getch(), if I can get it to work.
I added

#include <conio.h>

but gcc threw a fatal error:
conio.h: No such file or directory

If you're using Linux, then getch() doesn't officially exist. But google for
'linux getch' and there should be some code which will emulate it (probably
also kbhit(), another useful function).
 
B

BartC

DFS said:
On 05/15/2014 03:41 PM, BartC wrote:

I see that now!
Ideally, I want the user to enter Y or N (upper or lower case). Anything
else and they keep getting prompted for Y or N.

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"));

}
 
K

Keith Thompson

BartC said:
BTW if you have the getch() or _getch() function available, then you might
consider using that. (I probably assumed you were, I didn't look closely at
the char input line.)

This lets you type Y or N with a single key-press without needing to press
Enter (and you wouldn't have had that problem).

getch() should be on Windows, while there are also versions available (as a
fragment of source to download) for Linux.

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.)
 
B

BartC

Keith Thompson said:
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".

I was thinking of a version that emulates Windows' getch(), for example the
code here:

geezer.osdevbrasil.net/software/kbhit.c

I wouldn't get involved in curses/ncurses unless absolutely necessary.
 
B

Ben Bacarisse

BartC said:
I was thinking of a version that emulates Windows' getch(), for
example the code here:

geezer.osdevbrasil.net/software/kbhit.c

That's a slightly odd file. It's a .c file, but it defines the two key
functions (or procedures for those who use M-speak) as static.

There still seem to be quite a lot of tutorials or books or courses or
whatever that use this MSDOS-era style of interaction, so a slightly
more polished unix/linux resource would be useful. For example, it
should include a prototype for memcpy (or use an assignment), and it
prints error messages on stdout, rather than stderr.

<snip>
 
Ad

Advertisements

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"));

}


Thanks. I'll try it tomorrow.
 
B

BartC

Ben Bacarisse said:
That's a slightly odd file. It's a .c file, but it defines the two key
functions (or procedures for those who use M-speak) as static.

There still seem to be quite a lot of tutorials or books or courses or
whatever that use this MSDOS-era style of interaction, so a slightly
more polished unix/linux resource would be useful.

I didn't notice anything about it, other than that it worked!

The static functions sort of make sense, if they will only be used in the
module that includes that file (and won't interfere with any other uses of
kbhit/getch in the project).
For example, it
should include a prototype for memcpy (or use an assignment),

Looking at this line now:

memcpy(&new_kbd_mode, &g_old_kbd_mode, sizeof(struct termios));

I was thinking how much neater it would be if C allowed you to simply use an
assignment:

new_kbd_mode = old_kbd_mode;

then realised that it does! (Maybe the original author wasn't aware of it or
automatically used memcpy for any kind of block copy.)
 
B

Ben Bacarisse

BartC said:
I didn't notice anything about it, other than that it worked!

But then your are teaching (surely people will use this only when
leaning) including a .c file with function definitions. This is not
good style. If it were my class, I'd write a conio.h and provide a
"compile" command that silently includes the conio.o module (or a
makefile of whatever).

If you can't do that, I don't think its hard to tell people to "gcc -c
conio.c" once and to tack conio.o onto their compile commands.

I like to have people, when learning, turn up compiler warnings to
SCREAM AND SHOUT levels, and you can get annoying warnings for every
compile if you include a .c file that defines unused static functions
(or indeed anything else). You don't want warnings about code the
student hasn't written.
The static functions sort of make sense, if they will only be used in the
module that includes that file (and won't interfere with any other uses of
kbhit/getch in the project).


Looking at this line now:

memcpy(&new_kbd_mode, &g_old_kbd_mode, sizeof(struct termios));

I was thinking how much neater it would be if C allowed you to simply use an
assignment:

new_kbd_mode = old_kbd_mode;

then realised that it does! (Maybe the original author wasn't aware of it or
automatically used memcpy for any kind of block copy.)

Some things get stuck as idioms. Copying and zeroing OS control
structures using memcpy and memset seem to be one of these. You see it
all the time. It may date back to 1970's C when there was no struct
assignment, or it may be an overly cautious hang-over from OS calls that
have variable length structures.

The code is good, but would be properly useful with just a tiny bit of
tidying up and a README. People could then run old tutorial code on
free OSes unaltered.
 
Ad

Advertisements

B

BartC

Ben Bacarisse said:
But then your are teaching (surely people will use this only when
leaning) including a .c file with function definitions. This is not
good style.

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.

I didn't specify that particular one (except as an example to Keith), nor
how to use it.

(When I tested it, I think I just stuck a main() function at the end. But
the relevant code can be used in many ways including extracting it to use in
one's own library. Maybe even creating a conio.c, but I'm not familiar with
what else is expected to be in there.)
I like to have people, when learning, turn up compiler warnings to
SCREAM AND SHOUT levels, and you can get annoying warnings for every
compile if you include a .c file that defines unused static functions
(or indeed anything else). You don't want warnings about code the
student hasn't written.

I use function definitions inside an include file from time-to-time. But
then I'm using it as an actual include file and not a header file. C doesn't
really distinguish between them.
 

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