[net.lang.c] mixing scanf and gets

gwyn@brl-smoke.UUCP (04/03/86)

In article <1740@ittatc.ATC.ITT.UUCP> yoda@ittatc.ATC.ITT.UUCP (Todd C. Williams [Jedi Knight]) writes:
>Is it possible to use gets() after scanf() ?
>Is it a good idea to try to use gets() after scanf() ?
>It doesn't work, so I tried adding "fflush(stdin);" 
>just before the gets(), but that doesn't help either.
>If I do 2 "gets(str)"'s in a row, the second one works.
>
>Apparently, the scanf() is reading UP TO the newline, but
>leaving that newline in the buffer; the gets() sees the
>newline and thinks it is finished.

scanf() does whatever you tell it to with the input stream
then stops.  If you make it eat newlines, it will.  There
should be no problem mixing any of the STDIO input methods.

However, it sounds to me like your application would be
programmed better with the following scheme:

	while get-a-line-using-fgets
		parse-line-with-sscanf

By the way, don't use gets(); your input buffer can be
overrun if an input line is very long, resulting in
unpredictable malfunctioning of your application.  When
you use fgets(), it is often wise to test the last
character in the record and if it is a newline, replace
it with a 0 string terminator before parsing the record.

Since this advice is independent of UNIX, I'm cross-posting
to net.lang.c.

guy@sun.uucp (Guy Harris) (04/06/86)

> However, it sounds to me like your application would be
> programmed better with the following scheme:
> 
> 	while get-a-line-using-fgets
> 		parse-line-with-sscanf

From my experience, just about *any* application would be programmed better
using "fgets" and "sscanf" instead of "scanf"....

> By the way, don't use gets(); your input buffer can be
> overrun if an input line is very long, resulting in
> unpredictable malfunctioning of your application.  When
> you use fgets(), it is often wise to test the last
> character in the record and if it is a newline, replace
> it with a 0 string terminator before parsing the record.

And if it is NOT a newline, yell, scream, and bitch that somebody gave you a
line which was too long; otherwise, your program will think that the rest of
the line (i.e., everything after the cutoff) is an additional line.  The
SCCS "delta" command, when reading the output from "bdiff", didn't do this,
and got HORRIBLY confused if the new version of the file had a line longer
than about 510 characters long.
-- 
	Guy Harris
	{ihnp4, decvax, seismo, decwrl, ...}!sun!guy
	guy@sun.arpa	(yes, really)

gwyn@BRL.ARPA (VLD/VMB) (04/07/86)

Good point about treating non-newline fgets() data as an error;
besides a line too long, this might also result from the user
sending a delimiter such as EOT from a terminal.  (The SVID says
this, but SVR2 doesn't seem to act that way.)

rbj@icst-cmr (Root Boy Jim) (04/09/86)

	yoda@ittatc.ATC.ITT.UUCP (Todd C. Williams [Jedi Knight]) writes:
	>Is it possible to use gets() after scanf() ?
	>Is it a good idea to try to use gets() after scanf() ?
	>It doesn't work, so I tried adding "fflush(stdin);" 
	>just before the gets(), but that doesn't help either.
	>If I do 2 "gets(str)"'s in a row, the second one works.
	>
	>Apparently, the scanf() is reading UP TO the newline, but
	>leaving that newline in the buffer; the gets() sees the
	>newline and thinks it is finished.

So what's the problem? Use gets after scanf to eat the \n.
	
Doug Gwyn replys:

	scanf() does whatever you tell it to with the input stream
	then stops.  If you make it eat newlines, it will.  There
	should be no problem mixing any of the STDIO input methods.
	
	However, it sounds to me like your application would be
	programmed better with the following scheme:
	
		while get-a-line-using-fgets
			parse-line-with-sscanf
	
The way I understand it, scanf will eat input only as far as
makes sense, ungetc'ing the rest. The problem with sscanf is that
if your input only partially matches, there is no way to tell
where `the rest' begins. Or am I in outer space?

	By the way, don't use gets(); your input buffer can be
	overrun if an input line is very long, resulting in
	unpredictable malfunctioning of your application.  When

Agreed. Gets is `obseleted' by fgets.

	you use fgets(), it is often wise to test the last
	character in the record and if it is a newline, replace
	it with a 0 string terminator before parsing the record.
	
Which brings me to another point. Fgets is worthless on binary
data. It returns its first argument, which I already know.
If a null is part of the data, how do you know where it stopped
reading. Well if you're lucky, there will be a newline in there
and that's the end of it. But if you're reading blocks of nulls,
you're SOL. I would like fgets to return the number of chars read.

	(Root Boy) Jim Cottrell		<rbj@cmr>
	Baseball in D.C. in `87!

gwyn@BRL.ARPA (VLD/VMB) (04/09/86)

For reading binary data, don't use fgets(), which is designed for
text streams.  Use fread() instead.

jsdy@hadron.UUCP (Joseph S. D. Yao) (04/10/86)

In article <2476@brl-smoke.ARPA> rbj@icst-cmr (Root Boy Jim) writes:
>Which brings me to another point. Fgets is worthless on binary
>data. It returns its first argument, which I already know.
>	...  I would like fgets to return the number of chars read.

T h a t  is what fread() is for.  Use it.  Having an element-size
and a number-of-elements is useful when doing, e.g., structure I/O,
otherwise it is a bit of a pain, I'll agree.

[BTW, we have some decent pinball machines down at this end of
Montgomery County, tho I can't recall seeing Big Injun.]
-- 

	Joe Yao		hadron!jsdy@seismo.{CSS.GOV,ARPA,UUCP}

kwh@bentley.UUCP (KW Heuer) (04/10/86)

In article <2476@brl-smoke.ARPA> rbj@icst-cmr (Root Boy Jim) writes:
>The problem with sscanf is that if your input only partially matches,
>there is no way to tell where [the next input character is].

Agreed.  I'm somewhat surprised this isn't listed under "BUGS".  Of
course, in many applications you don't care (just output "syntax error"
and toss the line away); and it may be feasible to re-parse the entire
line with a new format in any case.

>Fgets is worthless on binary data.  It returns its first argument, which I
>already know.  If a null is part of the data, how do you know where it
>stopped reading.  Well if you're lucky, there will be a newline in there
>and that's the end of it.  But if you're reading blocks of nulls, you're
>SOL.  I would like fgets to return the number of chars read.

Clearly fgets isn't intended for binary data.  (Who writes line-oriented
binary data??)  Probably fread is what you want.  However, I think you're
right that nchars is a more useful return value (though I'd be satisfied
with a boolean).

Karl W. Z. Heuer (ihnp4!bentley!kwh), The Walking Lint

rbj@icst-cmr (root) (04/11/86)

> In article <2476@brl-smoke.ARPA> rbj@icst-cmr (Root Boy Jim) writes:
> >Fgets is worthless on binary data.  It returns its first argument, which I
> >already know.  If a null is part of the data, how do you know where it
> >stopped reading?  Well if you're lucky, there will be a newline in there
> >and that's the end of it.  But if you're reading blocks of nulls, you're
> >SOL.  I would like fgets to return the number of chars read.
> 
> Clearly fgets isn't intended for binary data.  (Who writes line-oriented
> binary data??)  Probably fread is what you want.  However, I think you're
> right that nchars is a more useful return value (though I'd be satisfied
> with a boolean).
> 
> Karl W. Z. Heuer (ihnp4!bentley!kwh), The Walking Lint

Allow me to clarify my position. I came across this while writing an
SMTP implementation. Most of what you see is text, altho ANY char
may be escaped with a backslash. It would have been easy enuf to look
for a '\n' that was not escaped and pass back the entire line.

Even with ascii data, I often find I want the count more often than I
want the first argument, which I already know. Perhaps it's designers
didn't know what to return, so they just punted & returned the first
argument because so many other stdio funxions did. This is
regrettable, because fgets has the char count right at hand.

	(Root Boy) Jim Cottrell		<rbj@cmr>