Discarding unread data after scanf()

A

Army1987

Is this a good way to discard unread data after scanf()?

while (getchar() != '\n')
;

According to the FAQ scanf always leaves the trailing newline on the input
stream, so there's no risk of discarding any more than needed. (In the best
case, when scanf() correctly works and the user hasn't input spurious data
beside those required in the control string, the loop will only iterate
once.)

(The only problem is if for some reason stdin is a file and it ends. But I
could well use drastic solutions such as
assert(!feof(stdin) && !ferror(stdin));
as the loop body, since if a program designed to be interactive reads from a
file which ends we can't do anything better than abort execution.)

--
#include <stdio.h>
#include <stdlib.h>
int main(void) /* Don't try this at home */ {
const size_t dim = 256; int i;
for (i=0; malloc(dim); i++) /*nothing*/ ;
printf("You're done! %zu\n", i*dim);
puts("\n\n--Army1987"); return 0;
}
 
P

pete

Army1987 said:
Is this a good way to discard unread data after scanf()?

A call to fscanf with stdin as the first argument,
is just like a call to scanf.

/* BEGIN fscanf_input.c */
/*
** There are only three different values
** that can be assigned to rc
** from the fscanf calls in this program.
** They are:
** EOF
** 0
** 1
** If rc equals EOF, then the end of file was reached,
** or there is some input problem;
** ferror and feof can be used to distinguish which.
** If rc equals 0, then an empty line was entered
** and the array contains garbage values.
** If rc equals 1, then there is a string in the array.
** Up to LENGTH number of characters are read
** from a line of a text stream
** and written to a string in an array.
** The newline character in the text line is replaced
** by a null character in the array.
** If the line is longer than LENGTH,
** then the extra characters are discarded.
*/
#include <stdio.h>

#define LENGTH 40
#define str(x) # x
#define xstr(x) str(x)

int main(void)
{
int rc;
char array[LENGTH + 1];

puts("The LENGTH macro is " xstr(LENGTH) ".");
do {
fputs("Enter any line of text to continue,\n"
"or just hit the Enter key to quit:", stdout);
fflush(stdout);
rc = fscanf(stdin, "%" xstr(LENGTH) "[^\n]%*[^\n]", array);
if (!feof(stdin)) {
getc(stdin);
}
if (rc == 0) {
array[0] = '\0';
}
if (rc == EOF) {
puts("rc equals EOF");
} else {
printf("rc is %d. Your string is:%s\n\n", rc, array);
}
} while (rc == 1);
return 0;
}

/* END fscanf_input.c */
 
A

Army1987

pete said:
A call to fscanf with stdin as the first argument,
is just like a call to scanf.
I know that. But that isn't what I was asking.

#include <stdio.h>
int input(const char *prompt)
{
int flag, result;
puts(prompt);
flag = scanf("%d", &result);
while (getchar() != '\n')
;
while (flag < 1) {
puts("Please enter an integer value");
flag = scanf("%d", &result);
while (getchar() != '\n')
;
}
return result;
}

Is this ok (supposing stdin is interactive)?
 
P

pete

Army1987 said:
fflush(stdout); /* forgot that... */

I don't think that fflush(stdout) is required
after a newline character is output.
puts always outputs a complete
line of text with newline character at the end.
I think of puts as creating a line of text from a string.

In case you don't know, a "line in a text stream"
is somewhat analagous to a "string in memory",
with a "line" being terminated by a newline character
while a "string" is terminated by a null character.

I misread your intial post as "Is there..."
when you has actually written "Is this..."

I will take another look at your code.
 
P

pete

Army1987 said:
Is this a good way to discard unread data after scanf()?

while (getchar() != '\n')
;

Your while loop will prevent endless looping from alpha input
while scanfing for int input.

/* BEGIN new_1.c output */

Please enter an integer value
ksdhfgkdashfkfrhwe
Please enter an integer value
wekjwerhiweuq
Please enter an integer value
wejuf
Please enter an integer value
wekjh
Please enter an integer value
ds
Please enter an integer value
42

The result is 42

/* END new_1.c output */

/* BEGIN new_2.c output */

Please enter an integer value
a
Please enter an integer value
Please enter an integer value
Please enter an integer value
Please enter an integer value
Please enter an integer value
Please enter an integer value ... and so on and so forth.

/* BEGIN new_1.c */

#include <stdio.h>

int input(const char *prompt);

int main(void)
{
puts("/* BEGIN new_1.c output */\n");
printf("\nThe result is %d\n",
input("Please enter an integer value"));
puts("\n/* END new_1.c output */");
return 0;
}

int input(const char *prompt)
{
int flag, result;

puts(prompt);
flag = scanf("%d", &result);
while (getchar() != '\n') {
;
}
while (flag < 1) {
puts("Please enter an integer value");
flag = scanf("%d", &result);
while (getchar() != '\n') {
;
}
}
return result;
}

/* END new_1.c */

/* BEGIN new_2.c */

#include <stdio.h>

int input(const char *prompt);

int main(void)
{
puts("/* BEGIN new_2.c output */\n");
printf("\nThe result is %d\n",
input("Please enter an integer value"));
puts("\n/* END new_2.c output */");
return 0;
}

int input(const char *prompt)
{
int flag, result;

puts(prompt);
flag = scanf("%d", &result);
while (flag < 1) {
puts("Please enter an integer value");
flag = scanf("%d", &result);
}
return result;
}

/* END new_2.c */
 
B

Barry Schwarz

Is this a good way to discard unread data after scanf()?

while (getchar() != '\n')
;

According to the FAQ scanf always leaves the trailing newline on the input
stream, so there's no risk of discarding any more than needed. (In the best
case, when scanf() correctly works and the user hasn't input spurious data
beside those required in the control string, the loop will only iterate
once.)

(The only problem is if for some reason stdin is a file and it ends. But I
could well use drastic solutions such as
assert(!feof(stdin) && !ferror(stdin));
as the loop body, since if a program designed to be interactive reads from a
file which ends we can't do anything better than abort execution.)

If you are worried about end of file as well as end of line you can
use something like
{int x; while (((x = getchar()) != '\n' ) && x != EOF) /* */;}


Remove del for email
 
K

Keith Thompson

Army1987 said:
Is this a good way to discard unread data after scanf()?

while (getchar() != '\n')
;
No.

According to the FAQ scanf always leaves the trailing newline on the input
stream, so there's no risk of discarding any more than needed. (In the best
case, when scanf() correctly works and the user hasn't input spurious data
beside those required in the control string, the loop will only iterate
once.)

What scanf leaves on the input stream depends on the format string.
With the right format string, it can consume the trailing newline.

In a later followup, you said you're using a "%d" format. In that
case, *if* you're going to use scanf() for input, then the above loop
with the addition of a check for EOF (see below) is reasonable,
assuming you want to accept just one integer value per input line.

But scanf() is usually not the best way to handle interactive input.
A better approach is to use fgets() to read an entire line at a time,
then use sscanf() to parse the line.

But even that has problems. If either scanf() or sscanf() with a "%d"
format attempts to read a number that won't fit in type int, the
behavior is undefined; there's no way to detect the error once it's
happened. If that's a concern, you might consider using strtol(),
which does detect and report overflow and underflow.

fgets() can also have problems with very long lines. I *think* this
is covered in the FAQ.
(The only problem is if for some reason stdin is a file and it ends. But I
could well use drastic solutions such as
assert(!feof(stdin) && !ferror(stdin));
as the loop body, since if a program designed to be interactive reads from a
file which ends we can't do anything better than abort execution.)

By "stdin is a file", I assume you mean that the program is reading
from a disk file rather than from, say, a keyboard. But you can
easily reach an end-of-file condition when reading from a keyboard.
<OT>On Unix-like systems, this is usually caused by the user typing
control-D; on DOS or Windows systems, it's usually control-Z.</OT You
should handle that case cleanly.

You can do much better than just aborting execution with a message
like:

assertion "!feof(stdin) && !ferror(stdin)" failed: file "c.c", line 6

You can test for end-of-file (preferably by comparing the result of
getchar() to EOF, not by calling feof() or ferror()), and you can
*gracefully* terminate the program. Quite often, reaching end-of-file
is the normal way for a program to terminate, simply because it has no
more work to do. In an interactive program that's expecting more
input, you can at least terminate with an error message that's going
to be meaningful to the user.
 
P

Peter Shaggy Haywood

Groovy hepcat Army1987 was jivin' on Sun, 25 Mar 2007 12:54:19 +0200
in comp.lang.c.
Discarding unread data after scanf()'s a cool scene! Dig it!
Is this a good way to discard unread data after scanf()?

while (getchar() != '\n')
;

No, not really. You need to test for EOF.
According to the FAQ scanf always leaves the trailing newline on the input
stream, so there's no risk of discarding any more than needed. (In the best
case, when scanf() correctly works and the user hasn't input spurious data
beside those required in the control string, the loop will only iterate
once.)

(The only problem is if for some reason stdin is a file and it ends.

Or it's an interactive device, and the user presses whatever key
combination (or takes whatever course of action) indicates an end of
file condition on the device. Or it's a punched tape reader, and the
tape breaks. Or it's an electroencephalograph, and the person whose
noggin it's wired up to has fallen asleep. Or it's any kind of device
ever invented by the mind and made by the hand of mankind, and
something goes wrong with it.
But I
could well use drastic solutions such as
assert(!feof(stdin) && !ferror(stdin));

Indeed that is a drastic solution. Don't do that. That's no way to
use assert(). It's for catching programming errors, not input errors.
When NDEBUG is defined, every use of assert() becomes a no-op. In this
situation you lose all your input error checking. And you don't want
to lose that.
Besides, there are better ways of determining whether there's a file
stream problem. Use the return value of the input function you're
using. All the file input functions return some indication of whether
or not they failed or tryed to read beyond the end of the file. For
example, getchar() returns EOF on failure or end of file. You can then
use ferror() or feof() to determine which of those two conditions
occurred.
as the loop body, since if a program designed to be interactive reads from a
file which ends we can't do anything better than abort execution.)

I recently posted a simple function that could do what you want. But
since you lurked here for some time before posting, as all good Usenet
denizens do, as they should, you must know that. I'm sure it just
slipped your mind. So I will refresh your memory. Here is the
function, slightly modified to return EOF on error or end of file:

#include <stdio.h>

int skip2nl(FILE *fp)
{
int c;

while(EOF != (c = fgetc(fp)) && '\n' != c)
;

return c;
}

You could use this function something like this:

int main(void)
{
int status;

puts("Enter some stuff on stdin, "
"and I will attempt to skip it...\n");

status = skip2nl(stdin);
if(EOF == status)
{
if(ferror(stdin))
{
puts("File error on stdin.");
}
else
{
puts("End of file on stdin.");
}
}
else
{
puts("Newline read from stdin.");
}

return 0;
}

--

Dig the even newer still, yet more improved, sig!

http://alphalink.com.au/~phaywood/
"Ain't I'm a dog?" - Ronny Self, Ain't I'm a Dog, written by G. Sherry & W. Walker.
I know it's not "technically correct" English; but since when was rock & roll "technically correct"?
 

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

No members online now.

Forum statistics

Threads
473,764
Messages
2,569,564
Members
45,039
Latest member
CasimiraVa

Latest Threads

Top