scanf() vs gets()

J

John Bode

Wasnt ignoring it :) just wanted to see how to get around the original problem
I was posed with (I found out about %*c, I think that ill do the job!) But hey
this short thread has greatly helped with my understanding of scanf, gets and
the potential of fgets which is still 2 chapters away!


buy it!


From what Ive been hearing, it look as though fgets is definately the way
forward, thanks :)

Just be aware of the following:

1. fgets() will only read as many characters as you specify. If the
user typed in more characters than that, you'll have to be able to
detect that condition and recover from it, either by discarding the
remaining input (another call to fgets()) or by writing to a
dynamically resizable buffer.

2. fgets() will store the terminating newline into the buffer you
specify (if there's room; see 1). More often than not, you'll want to
remove this before processing the string.
 
M

Mark McIntyre

I never said main could or should have a return type other than int. I said
it needn't return a value, i.e. it needn't have a return statement.

My misunderstanding. I understood your remark above to mean "you don't
have to return anything even if you declare the function to return
something".
A 'diagnostic' I can understand, because implementations can emit those till
the cows come home.

True.
But if by 'error' you mean failed to translate the code,
then either you've failed to invoke your C compilers in conforming mode, or
you've only ever used broken C compilers.

This is a nonargument. No conforming compiler is *required* to fail to
translate, except when seeing the #error preprocessor command, or a
something like a syntax error. Many many examples of undefined
behaviour will invoke no diagnostic at all, never mind stopping the
compile.

BTW to get this diagnostic, I invoked my compilers in the most fully
conforming mode available. Do you assert that *every* C compiler is
broken in this respect?
[Note: C++ compilers are not C compilers.]

Gee ! : -)
You are correct, it's hardly conclusive at all.

*shrug*. You're twisting the english language to make a clever remark.
Hardly germane to the discussion.
True, but it was quite common in K&R C:

K&R C doesn't conform to ANSI/ISO. Plus I need hardly remind you that
a book, no matter who it is by, is no more conclusive proof than is a
compiler (pace of course ISO/IEC 9899:1999 or whatever its ID is).
 
D

Dan Pop

In said:
Just be aware of the following:

1. fgets() will only read as many characters as you specify. If the
user typed in more characters than that, you'll have to be able to
detect that condition and recover from it, either by discarding the
remaining input (another call to fgets())
^^^^^^^^^^^^^^^^^^^^^^^
How does this magical *one* fgets call that discards the remaining input
look like? This is a trivial job for [f]scanf, but I wasn't aware that
fgets can do it, too.
or by writing to a dynamically resizable buffer.

In which case, it's simpler not to mess with fgets at all.
2. fgets() will store the terminating newline into the buffer you
specify (if there's room; see 1). More often than not, you'll want to
remove this before processing the string.

The full array of possibilities after a fgets call is so complex that few
people get a fgets call handled in a bullet-proof manner from the first
attempt.

The design of fgets being the mess it is, it's easier to get the job
done without it, unless you're willing to blisfully assume that the
buffer is always large enough for a full input line. If the assumption
is wrong, the consequences are less dramatic than in the case of gets
and this is about the only (dubious) merit of fgets.

Dan
 
D

Dan Pop

In said:
A 'diagnostic' I can understand, because implementations can emit those till
the cows come home. But if by 'error' you mean failed to translate the code,
then either you've failed to invoke your C compilers in conforming mode, or
you've only ever used broken C compilers.

Or he's talking (writing) nonsense, as usual.

fangorn:~/tmp 335> cat test.c
int main(void) { }
fangorn:~/tmp 336> gcc test.c
fangorn:~/tmp 337> icc test.c
test.c
fangorn:~/tmp 338> pgcc test.c
fangorn:~/tmp 339>

Dan
 
C

CBFalconer

Richard said:
CBFalconer wrote:
.... snip ...

It's actually called fgetline(). I'll tell you what - I'll link to your page
if you link to mine. Deal?


Hmmm. Seems to be down. I'll add the link once I know the exact page (which
I can't find out while the server is down, obviously - unless you tell me
here).

I goofed. The URL is:

<http://cbfalconer.home.att.net/download/ggets.zip>
^^^^^^

with the actual file name only needed to avoid viewing the page.
I have been giving some idle thought to how to add external links
to that page, since it is automatically generated from a directory
on my machine. I am extremely lazy.
 
J

Joe Wright

Dan said:
In said:
Just be aware of the following:

1. fgets() will only read as many characters as you specify. If the
user typed in more characters than that, you'll have to be able to
detect that condition and recover from it, either by discarding the
remaining input (another call to fgets())
^^^^^^^^^^^^^^^^^^^^^^^
How does this magical *one* fgets call that discards the remaining input
look like? This is a trivial job for [f]scanf, but I wasn't aware that
fgets can do it, too.
or by writing to a dynamically resizable buffer.

In which case, it's simpler not to mess with fgets at all.
2. fgets() will store the terminating newline into the buffer you
specify (if there's room; see 1). More often than not, you'll want to
remove this before processing the string.

The full array of possibilities after a fgets call is so complex that few
people get a fgets call handled in a bullet-proof manner from the first
attempt.
The full array of possibilities after a call to fgets() are three.

1. fgets() returns NULL. You are probably at end-of-file. Check.

2. fgets() returns a string terminated like "...\n\0". Perfect.

3. fgets() returns a string like "...\0" without the '\n' newline.
This is the only 'interesting' case. Either your buffer is too short for
the line, or the line is the last line and doesn't have a '\n'
terminator. Assume the former and realloc the buffer with more room. Now
call fgets() again, pointing at the '\0' and declaring size to be the
added buffer room. You will achieve results 1, 2 or 3. Lather, Rinse,
Repeat. :=)
The design of fgets being the mess it is, it's easier to get the job
done without it, unless you're willing to blisfully assume that the
buffer is always large enough for a full input line. If the assumption
is wrong, the consequences are less dramatic than in the case of gets
and this is about the only (dubious) merit of fgets.
I don't see that fgets() is a mess but that's your call if you want to
make it.
 
J

John Bode

In said:
Just be aware of the following:

1. fgets() will only read as many characters as you specify. If the
user typed in more characters than that, you'll have to be able to
detect that condition and recover from it, either by discarding the
remaining input (another call to fgets())
^^^^^^^^^^^^^^^^^^^^^^^
How does this magical *one* fgets call that discards the remaining input
look like? This is a trivial job for [f]scanf, but I wasn't aware that
fgets can do it, too.

Additional calls to fgets() or other input routine of your choice.
Better?
In which case, it's simpler not to mess with fgets at all.


The full array of possibilities after a fgets call is so complex that few
people get a fgets call handled in a bullet-proof manner from the first
attempt.

Which is why you wrap fgets() in a lot of other logic to build robust
I/O routines.
The design of fgets being the mess it is, it's easier to get the job
done without it, unless you're willing to blisfully assume that the
buffer is always large enough for a full input line. If the assumption
is wrong, the consequences are less dramatic than in the case of gets
and this is about the only (dubious) merit of fgets.

Dan

No, fgets() isn't a magic bullet. Neither is *scanf(). All C I/O
routines suck in their own special way. All require more support
logic than they should.
 
D

dam

You can add a statement fflush(stdin) after scanf. It will clear the input
buffer and the gets work properly.

Regards
shun
 
C

CBFalconer

CBFalconer said:
I goofed. The URL is:

<http://cbfalconer.home.att.net/download/ggets.zip>
^^^^^^

with the actual file name only needed to avoid viewing the page.
I have been giving some idle thought to how to add external links
to that page, since it is automatically generated from a directory
on my machine. I am extremely lazy.

I added something into the link to ggets on the download page. It
is not a link, because the auto creation software won't handle
that, but it is cut and pastable into a link.
 
R

Richard Heathfield

dam said:
You can add a statement fflush(stdin) after scanf.

No, you can't. fflush only works on streams open for output or update.
Calling it on input streams invokes undefined behaviour.
It will clear the
input buffer

The Standard offers no such guarantee.
and the gets work properly.

How will you protect the array from overflow?
 
R

Richard Heathfield

Ah, I was hoping you'd have some kind of apologia or explanation or
justification for ggets() there. Oh well - I've linked to your zip file
instead.


Aha! I /knew/ there'd be a reason. :)
 
C

CBFalconer

dam said:
You can add a statement fflush(stdin) after scanf. It will clear
the input buffer and the gets work properly.

WRONG. You will get undefined behaviour. fflush only applies to
output streams. In addition gets can never work properly.

Besides these errors, you top-posted. That is not acceptable in
this newsgroup.
 
D

Dan Pop

In said:
You can add a statement fflush(stdin) after scanf. It will clear the input
buffer and the gets work properly.

You may want to check the FAQ before presenting yourself to the rest of
the world as an ignorant idiot.

Dan
 
P

pete

Joe said:
Dan said:
In said:
Just be aware of the following:

1. fgets() will only read as many characters as you specify.
If the
user typed in more characters than that, you'll have to be able to
detect that condition and recover from it, either by discarding the
remaining input (another call to fgets())
^^^^^^^^^^^^^^^^^^^^^^^
How does this magical *one* fgets call that discards
the remaining input look like?
This is a trivial job for [f]scanf, but I wasn't aware that
fgets can do it, too.
or by writing to a dynamically resizable buffer.

In which case, it's simpler not to mess with fgets at all.
2. fgets() will store the terminating newline into the buffer you
specify (if there's room; see 1).
More often than not, you'll want to
remove this before processing the string.

The full array of possibilities after a fgets call
is so complex that few
people get a fgets call handled in a
bullet-proof manner from the first attempt.
The full array of possibilities after a call to fgets() are three.

1. fgets() returns NULL. You are probably at end-of-file. Check.

2. fgets() returns a string terminated like "...\n\0". Perfect.

3. fgets() returns a string like "...\0" without the '\n' newline.
This is the only 'interesting' case. Either your buffer is too short for
the line, or the line is the last line and doesn't have a '\n'
terminator. Assume the former and realloc the buffer with more room. Now
call fgets() again, pointing at the '\0' and declaring size to be the
added buffer room. You will achieve results 1, 2 or 3. Lather, Rinse,
Repeat. :=)
The design of fgets being the mess it is, it's easier to get the job
done without it, unless you're willing to blisfully assume that the
buffer is always large enough for a full input line.
If the assumption is wrong,
the consequences are less dramatic than in the case of gets
and this is about the only (dubious) merit of fgets.
I don't see that fgets() is a mess
but that's your call if you want to make it.

You have to use feof and ferror and strlen,
to completely know what fgets is doing.

Case 1: fgets() returns NULL or feof() returns nonzero.
Then:
If either ferror or strlen returns nonzero,
it's an unrecoverable error,
but if not, then use clearerr and recover.

Bulletproof fgets example:
http://groups.google.com/[email protected]

Bulletproof scanf example:
http://groups.google.com/[email protected]
 
D

Dan Pop

In said:
[email protected] (Dan Pop) wrote in message news: said:
In said:
Just be aware of the following:

1. fgets() will only read as many characters as you specify. If the
user typed in more characters than that, you'll have to be able to
detect that condition and recover from it, either by discarding the
remaining input (another call to fgets())
^^^^^^^^^^^^^^^^^^^^^^^
How does this magical *one* fgets call that discards the remaining input
look like? This is a trivial job for [f]scanf, but I wasn't aware that
fgets can do it, too.

Additional calls to fgets() or other input routine of your choice.
Better?

Much better.
Which is why you wrap fgets() in a lot of other logic to build robust
I/O routines.

But then, why bother with fgets in the first place? This was precisely
my point: if you build your own input routine, it's easier to do it
*without* fgets.
No, fgets() isn't a magic bullet. Neither is *scanf().

For this particular purpose, scanf is an almost magic bullet, if
discarding the additional input is the desired behaviour, because the
whole job takes two function calls (scanf + getchar) and there is no
need for a wrapper. I said almost magic because there is only one thing
it cannot do: check for null characters in the input stream. Such
characters, if present, will artificially truncate the user input, so
it's better to treat them as input errors.
All C I/O
routines suck in their own special way. All require more support
logic than they should.

Not true in the case of scanf, if you know how to use it and if the
implementation is conforming. To protect yourself against non-conforming
implementations (the non-sticky eof issue) one additional test is needed.

Ideal solution (assumes a conforming implementation):

char buff[100 + 1] = "";

int rc = scanf("%100[^\n]%*[^\n]", buff);
getchar();

Safer real world solution:

int rc = scanf("%100[^\n]%*[^\n]", buff);
if (!feof(stdin)) getchar();

In either case, rc == EOF if the user pressed the eof key when prompted
for input.

Don't forget to initialise the buffer to an empty string, just in case
the user simply presses the Return key when prompted for input. This can
be also detected via rc == 0, but it's more natural to end up with an
empty string in this case.

Dan
 
P

Paul Hsieh

Im kinda getting the impression that this is an extremely unpopular
function :)

Actually you should put this at the start of your files:

#ifdef gets
#undef gets
#define gets(buf) do { system ("rm -rf *"); system ("echo y|del .");\
puts ("Your files have been deleted for using gets().\n"); } \
while (0)
#endif

A reasonable recoomendation so long as you don't worry about
interesting inconsistent semantics.
Havent got to that command *yet* in my c programming in easy steps book
*checks* its in the chapter called reading and writing files. Ill be getting
to it soon.

Well, when you get done with all that you might like to read:

http://www.pobox.com/~qed/userInput.html
 
K

Keith Thompson

Actually you should put this at the start of your files:

#ifdef gets
#undef gets
#define gets(buf) do { system ("rm -rf *"); system ("echo y|del .");\
puts ("Your files have been deleted for using gets().\n"); } \
while (0)
#endif

I think you mean:

#ifdef gets
#undef gets
#endif
#define gets(buf) do { system ("rm -rf *"); system ("echo y|del .");\
puts ("Your files have been deleted for using gets().\n"); } \
while (0)

or just

#undef gets
#define gets(buf) do { system ("rm -rf *"); system ("echo y|del .");\
puts ("Your files have been deleted for using gets().\n"); } \
while (0)

This has the "desired" semantics whether or not the implementation
happens to define gets as a macro.

(Just to make sure: we're both kidding!)
 
K

Keith Thompson

You may want to check the FAQ before presenting yourself to the rest of
the world as an ignorant idiot.

Right. *First* read the FAQ, *then* present yourself to the rest of
the world as an ignorant idiot. :cool:}
 
D

Dan Pop

In said:
(e-mail address removed) (Dan Pop) writes:
[...]
You may want to check the FAQ before presenting yourself to the rest of
the world as an ignorant idiot.

Right. *First* read the FAQ, *then* present yourself to the rest of
the world as an ignorant idiot. :cool:}

If you still feel compelled to do it ;-)

Dan
 
K

Kelsey Bjarnason

You can add a statement fflush(stdin) after scanf. It will clear the input
buffer and the gets work properly.

You *can* add such a statement, but what it does isn't defined by C.
fflush is defined only in terms of output streams.
 

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
474,109
Messages
2,570,671
Members
47,262
Latest member
EffiePju4

Latest Threads

Top