scanf/getchar sequence problem

C

clusardi2k

/*
The below code on SGI will wait for you to enter 2 things, but
on Linux it will only wait the first time.

I can make the code work by replacing the scanf with:

char data [5];
fgets (data,5,stdin);
the_number = atoi (data);

But, instead I would like to modify the scanf, "/dev/tty", or
something simple like that.
*/

#include <stdio.h>
int main()
{
int c;
int the_number;
fprintf (stderr,"Enter Number :");
scanf ("%d",&the_number);
fprintf (stderr,"\nEnter a letter :");
c = getchar ();
if ( (c = getchar ()) == EOF )
freopen ("/dev/tty","r",stdin);
else
ungetc (c,stdin);
return (0);
}
 
E

Eric Sosman

/*
The below code on SGI will wait for you to enter 2 things, but
on Linux it will only wait the first time.

I can make the code work by replacing the scanf with:

char data [5];
fgets (data,5,stdin);
the_number = atoi (data);

But, instead I would like to modify the scanf, "/dev/tty", or
something simple like that.
*/

#include <stdio.h>
int main()
{
int c;
int the_number;
fprintf (stderr,"Enter Number :");
scanf ("%d",&the_number);
fprintf (stderr,"\nEnter a letter :");
c = getchar ();
if ( (c = getchar ()) == EOF )
freopen ("/dev/tty","r",stdin);
else
ungetc (c,stdin);
return (0);
}

Linux is right; SGI is wrong. The scanf() should consume
any initial white space, an optional sign character, and a
digit string, but should not consume the following '\n' (or
whatever else follows the digits). The subsequent getchar()
should retrieve the '\n' (or whatever).

My advice is to avoid scanf() for interactive input: it
can be tricky to use, and recovery from input errors is
difficult (consider what happens if the user enters "#\n"
at your first prompt). It is usually better to proceed along
the lines of the "cure" you discovered: Read the line with
fgets() and then interpret the characters yourself. It's
better to use strtod() or sscanf() than atoi(), because the
latter has essentially *no* way to diagnose erroneous input.
 
J

John Carson

Eric Sosman said:
/*
The below code on SGI will wait for you to enter 2 things, but
on Linux it will only wait the first time.

I can make the code work by replacing the scanf with:

char data [5];
fgets (data,5,stdin);
the_number = atoi (data);

But, instead I would like to modify the scanf, "/dev/tty", or
something simple like that.
*/

#include <stdio.h>
int main()
{
int c;
int the_number;
fprintf (stderr,"Enter Number :");
scanf ("%d",&the_number);
fprintf (stderr,"\nEnter a letter :");
c = getchar ();
if ( (c = getchar ()) == EOF )
freopen ("/dev/tty","r",stdin);
else
ungetc (c,stdin);
return (0);
}

Linux is right; SGI is wrong. The scanf() should consume
any initial white space, an optional sign character, and a
digit string, but should not consume the following '\n' (or
whatever else follows the digits). The subsequent getchar()
should retrieve the '\n' (or whatever).

Yes, but note that there are two getchar() calls in the code, so it should
be possible to enter a char at the console (though the effect will not be
the intended one).

I wonder if the code run on Linux and the code run on SGI were really the
same.
 
E

Eric Sosman

John said:
[...] The subsequent getchar()
should retrieve the '\n' (or whatever).

Yes, but note that there are two getchar() calls in the code, so it should
be possible to enter a char at the console (though the effect will not be
the intended one).

Oh, foozle. I'd like to say "That'll teach me not
to speed-read code," but experience suggests it won't ...
 
S

S.Tobias

In said:
/*
The below code on SGI will wait for you to enter 2 things, but
on Linux it will only wait the first time.
[snip]
fprintf (stderr,"Enter Number :");
scanf ("%d",&the_number);
fprintf (stderr,"\nEnter a letter :");
c = getchar ();
if ( (c = getchar ()) == EOF )
[snip]

On Linux works as expected, ie. waits or not depending
on what data you feed (at least at an Xterm). Try to
output values of `c' to see what happens (could it be
that Enter generates more than one character?)
 
C

clusardi2k

Eric said:
/*
The below code on SGI will wait for you to enter 2 things, but
on Linux it will only wait the first time.

I can make the code work by replacing the scanf with:

char data [5];
fgets (data,5,stdin);
the_number = atoi (data);

But, instead I would like to modify the scanf, "/dev/tty", or
something simple like that.
*/

#include <stdio.h>
int main()
{
int c;
int the_number;
fprintf (stderr,"Enter Number :");
scanf ("%d",&the_number);
fprintf (stderr,"\nEnter a letter :");
c = getchar ();
if ( (c = getchar ()) == EOF )
freopen ("/dev/tty","r",stdin);
else
ungetc (c,stdin);
return (0);
}

Linux is right; SGI is wrong. The scanf() should consume
any initial white space, an optional sign character, and a
digit string, but should not consume the following '\n' (or
whatever else follows the digits). The subsequent getchar()
should retrieve the '\n' (or whatever).

This is solution temporarily OK'ed by boss:

#include <stdio.h>
int main()
{
int c;
int the_number;
char car_ret;

fprintf (stderr,"Enter Number :");

#ifdef __sgi
scanf ("%d",&the_number);
#elif defined (__linux)
scanf ("%d%c",&the_number,&car_ret);
#endif

fprintf (stderr,"\nEnter a letter :");
c = getchar ();
if ( (c = getchar ()) == EOF )
freopen ("/dev/tty","r",stdin);
else
ungetc (c,stdin);
return (0);
}
 
E

Eric Sosman

[scanf("%d") leaves the newline unconsumed]

This is solution temporarily OK'ed by boss:

#include <stdio.h>
int main()
{
int c;
int the_number;
char car_ret;

fprintf (stderr,"Enter Number :");

#ifdef __sgi
scanf ("%d",&the_number);
#elif defined (__linux)
scanf ("%d%c",&the_number,&car_ret);
#endif

Fragile; a house of cards. If the user enters anything
at all between the final digit and the newline -- a space
character, say -- you'll be right back where you started.
I'll repeat my earlier advice: scanf() is not well suited
for interactive input, and you'd be better off using another
method altogether.
 
P

pete

Eric said:
[scanf("%d") leaves the newline unconsumed]

This is solution temporarily OK'ed by boss:

#include <stdio.h>
int main()
{
int c;
int the_number;
char car_ret;

fprintf (stderr,"Enter Number :");

#ifdef __sgi
scanf ("%d",&the_number);
#elif defined (__linux)
scanf ("%d%c",&the_number,&car_ret);
#endif

Fragile; a house of cards. If the user enters anything
at all between the final digit and the newline -- a space
character, say -- you'll be right back where you started.
I'll repeat my earlier advice: scanf() is not well suited
for interactive input, and you'd be better off using another
method altogether.

Is stderr well suited for interactive output?
I always thought that the purpose of the standard error stream
was to output error messages.
 
K

Karl Heinz Buchegger

Eric said:
I'll repeat my earlier advice: scanf() is not well suited
for interactive input, and you'd be better off using another
method altogether.

And it is a very good advice. The OP should follow it.
The substitution of scanf with a fgets() sscanf() pair isn't
much work and saves you from that problem (and others
you have not discovered yet).

char Buffer[ BUFFER_SIZE ]
fgets( Buffer, sizeof( Buffer ), stdin );
sscanf( "... whatever you want

The only problem is the BUFFER_SIZE. It doesn't matter which number
you choose, there is always the possibility that some user might overflow
it. If you set it large enough, say 4096, that probability is very low (although
not 0.0). In such cases it is often sufficient to just detect that fact (which
can easily be done: just look for a '\n' character in Buffer) and ask the user
for less input.
 
C

CBFalconer

Eric said:
[scanf("%d") leaves the newline unconsumed]

This is solution temporarily OK'ed by boss:

#include <stdio.h>
int main()
{
int c;
int the_number;
char car_ret;

fprintf (stderr,"Enter Number :");

#ifdef __sgi
scanf ("%d",&the_number);
#elif defined (__linux)
scanf ("%d%c",&the_number,&car_ret);
#endif

Fragile; a house of cards. If the user enters anything
at all between the final digit and the newline -- a space
character, say -- you'll be right back where you started.
I'll repeat my earlier advice: scanf() is not well suited
for interactive input, and you'd be better off using another
method altogether.

Actually, used properly scanf is quite suitable, as long as you are
willing to have the overhead of that large interpreter.

/* get a numeric and flush the input line */
/* return 0 for success, non-zero otherwise */
int getnum(int *num)
{
int ok, ch;

ok = scanf("%d", num);
while (EOF != (ch = getchar()) && ('\n' != ch)) continue;
return (1 == ok);
}

although I would never use it in that form. I keep a separate
flushln(f) function about, and would simply write:

if (1 == scanf("%d", &num)) .....
if (EOF == flushln(stdin)) .....

as long as people realize that leading blanks and empty lines will
be skipped. The key to suitable use of scanf interactively is the
"if (1 == scanf(....". Anything other than one leads to confusion,
gnashing of teeth, and loss of hair. The advantage is the
elimination of presized buffers.
 
R

Richard Herring

pete <[email protected]> said:
Eric Sosman wrote: [...]
I'll repeat my earlier advice: scanf() is not well suited
for interactive input, and you'd be better off using another
method altogether.

Is stderr well suited for interactive output?
I always thought that the purpose of the standard error stream
was to output error messages.
Is asking random questions about stderr well suited for eliciting advice
about scanf()?

scanf (and its friends) take a format string as an argument. That
implies that the _format_ of the expected input is known. Can you
guarantee that for interactive input? If not, then scanf is the wrong
tool.
 
R

Richard Herring

[QUOTE="pete said:
In message <[email protected]>,
pete <[email protected]>
Is asking random questions about
stderr well suited for eliciting advice about scanf()?

I doubt it.
I was asking about the example code which you snipped.
[/QUOTE]

Oh, I see. Well, the answer would be "yes", though personally, since
this is crossposted to clc++ I'd prefer cerr.

The user may have redirected stdout to a file or piped it to another
program, so they wouldn't see prompts directed to it. And stderr is
automatically flushed, so if you're lucky the prompts will appear before
the input operation starts.
 
P

pete

Richard said:
[QUOTE="pete said:
Is stderr well suited for interactive output?
I always thought that the purpose of the standard error stream
was to output error messages.

Is asking random questions about
stderr well suited for eliciting advice about scanf()?

I doubt it.
I was asking about the example code which you snipped.
#include <stdio.h>
int main()
{
int c;
int the_number;
fprintf (stderr,"Enter Number :");
scanf ("%d",&the_number);
fprintf (stderr,"\nEnter a letter :");

Oh, I see. Well, the answer would be "yes", though personally, since
this is crossposted to clc++ I'd prefer cerr.

The user may have redirected stdout to a file or piped it to another
program, so they wouldn't see prompts directed to it. And stderr is
automatically flushed,
so if you're lucky the prompts will appear before
the input operation starts.[/QUOTE]

If I understand you correctly,
you consider stderr to be a superior FILE* choice to stdout,
for the above code example.
You prefer stderr for prompting in general
and stdout for whatever calculated output the program has.
Right?
 
R

Richard Herring

[QUOTE="pete said:
Oh, I see. Well, the answer would be "yes", though personally, since
this is crossposted to clc++ I'd prefer cerr.

The user may have redirected stdout to a file or piped it to another
program, so they wouldn't see prompts directed to it. And stderr is
automatically flushed,
so if you're lucky the prompts will appear before
the input operation starts.

If I understand you correctly,
you consider stderr to be a superior FILE* choice to stdout,
for the above code example.
You prefer stderr for prompting in general
and stdout for whatever calculated output the program has.
Right?
[/QUOTE]
Right.
 
P

pete

Richard said:
[QUOTE="pete said:
In message <[email protected]>, pete <[email protected]>
writes
Richard Herring wrote:

In message <[email protected]>,
pete <[email protected]>

Is stderr well suited for interactive output?
I always thought that the purpose of the standard error stream
was to output error messages.

Is asking random questions about
stderr well suited for eliciting advice about scanf()?

I doubt it.
I was asking about the example code which you snipped.

#include <stdio.h>
int main()
{
int c;
int the_number;
fprintf (stderr,"Enter Number :");
scanf ("%d",&the_number);
fprintf (stderr,"\nEnter a letter :");

Oh, I see. Well, the answer would be "yes", though personally, since
this is crossposted to clc++ I'd prefer cerr.

The user may have redirected stdout to a file or piped it to another
program, so they wouldn't see prompts directed to it. And stderr is
automatically flushed,
so if you're lucky the prompts will appear before
the input operation starts.

If I understand you correctly,
you consider stderr to be a superior FILE* choice to stdout,
for the above code example.
You prefer stderr for prompting in general
and stdout for whatever calculated output the program has.
Right?
Right.[/QUOTE]

Thank you.
 
K

Karl Heinz Buchegger

Richard said:
Oh, I see. Well, the answer would be "yes", though personally, since
this is crossposted to clc++ I'd prefer cerr.

The user may have redirected stdout to a file or piped it to another
program, so they wouldn't see prompts directed to it. And stderr is
automatically flushed, so if you're lucky the prompts will appear before
the input operation starts.

Hmm. I was going to post a reply which headed in the same direction.
But then I noticed something.
Redirecting output is fine. But the user could also redirect input.
So while he sees the prompt, the answer to that prompt would come
from the redirected input. Unfortunately there is no second input
stream like there is for output.
So in the end I came to the conclusion that doing *interactive
output* is not really a good idea to handle through stderr.
If a program needs interactive input, then redirecting the input/output
might become a real nightmare.

Any comments?
 
R

Richard Herring

Karl Heinz Buchegger said:
Hmm. I was going to post a reply which headed in the same direction.
But then I noticed something.
Redirecting output is fine. But the user could also redirect input.
So while he sees the prompt, the answer to that prompt would come
from the redirected input. Unfortunately there is no second input
stream like there is for output.
So in the end I came to the conclusion that doing *interactive
output* is not really a good idea to handle through stderr.
If a program needs interactive input, then redirecting the input/output
might become a real nightmare.

Real and robust programs that do that sort of thing usually start by
detecting (nonstandardly?) whether stdin comes from a terminal or
something else, and suppress the prompts if it's not a terminal.
Any comments?

For serious amounts of interaction I'd prefer a GUI anyway, unless there
are hardware constraints.
 
R

Richard Bos

Richard Herring said:
Oh, I see. Well, the answer would be "yes", though personally, since
this is crossposted to clc++ I'd prefer cerr.

The user may have redirected stdout to a file or piped it to another
program, so they wouldn't see prompts directed to it. And stderr is
automatically flushed, so if you're lucky the prompts will appear before
the input operation starts.

If, yes. If you're unlucky, though, stderr will appear in the console
window, not in the user's main window, and he'll completely overlook it.
The sysadmin, OTOH, will be pissed off at you for spamming his log files
with completely meaningless messages. stderr is for error messages, not
for prompts. Besides, if stdout _is_ redirected, that was intentional.

Richard
 
R

Richard Herring

Richard Bos said:
If, yes. If you're unlucky, though, stderr will appear in the console
window,

What is a "console window"?
not in the user's main window, and he'll completely overlook it.

Just as he would if he redirected stdout to a file, or piped it to
another program.
The sysadmin,

What is a "sysadmin"?
OTOH, will be pissed off at you for spamming his log files
with completely meaningless messages.

Even back in the days when I had to manage a mainframe system, I don't
recall messages to stderr from user programs ever going anywhere but the
user's teletype. But yes, it's true that the way the standard streams
are connected to the hardware is beyond the scope of the C or C++
standards, so anything could happen.
stderr is for error messages, not
for prompts.

And stdout is for computed output, not for prompts. In the absence of a
stdprompt, one has to use one or the other, and the decision is not best
made on the basis of its name but on how it interacts with the outside
world.
Besides, if stdout _is_ redirected, that was intentional.

Of course. It's not something one does by accident. But intentions
differ.
 

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

Members online

Forum statistics

Threads
473,756
Messages
2,569,535
Members
45,008
Latest member
obedient dusk

Latest Threads

Top