Discarding unread data after scanf()

Discussion in 'C Programming' started by Army1987, Mar 25, 2007.

  1. Army1987

    Army1987 Guest

    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;
    }
    Army1987, Mar 25, 2007
    #1
    1. Advertising

  2. Army1987

    pete Guest

    Army1987 wrote:
    >
    > 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 */


    --
    pete
    pete, Mar 25, 2007
    #2
    1. Advertising

  3. Army1987

    Army1987 Guest

    "pete" <> ha scritto nel messaggio
    news:...
    > Army1987 wrote:
    >>
    >> 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.
    >

    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)?
    Army1987, Mar 25, 2007
    #3
  4. Army1987

    Army1987 Guest

    "Army1987" <> ha scritto nel messaggio
    news:eu5tm5$bog$...
    > #include <stdio.h>
    > int input(const char *prompt)
    > {
    > int flag, result;
    > puts(prompt);

    fflush(stdout); /* forgot that... */
    > flag = scanf("%d", &result);
    > while (getchar() != '\n')
    > ;
    > while (flag < 1) {
    > puts("Please enter an integer value");

    fflush(stdout);
    > flag = scanf("%d", &result);
    > while (getchar() != '\n')
    > ;
    > }
    > return result;
    > }
    >
    > Is this ok (supposing stdin is interactive)?
    >
    Army1987, Mar 25, 2007
    #4
  5. Army1987

    pete Guest

    Army1987 wrote:

    > > puts(prompt);

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

    --
    pete
    pete, Mar 25, 2007
    #5
  6. Army1987

    pete Guest

    Army1987 wrote:
    >
    > 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 */

    --
    pete
    pete, Mar 25, 2007
    #6
  7. On Sun, 25 Mar 2007 12:54:19 +0200, "Army1987" <>
    wrote:

    >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
    Barry Schwarz, Mar 25, 2007
    #7
  8. "Army1987" <> writes:
    > 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.

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
    "We must do something. This is something. Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
    Keith Thompson, Mar 25, 2007
    #8
  9. 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"?
    Peter Shaggy Haywood, Mar 28, 2007
    #9
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. =?Utf-8?B?Q2luY2lu?=

    Identify Unread Posts in Forum-like Application

    =?Utf-8?B?Q2luY2lu?=, Dec 1, 2005, in forum: ASP .Net
    Replies:
    2
    Views:
    390
    =?Utf-8?B?Q2luY2lu?=
    Dec 1, 2005
  2. a
    Replies:
    2
    Views:
    397
    Stargaming
    Aug 13, 2006
  3. =?ISO-8859-1?Q?Martin_J=F8rgensen?=

    scanf (yes/no) - doesn't work + deprecation errors scanf, fopen etc.

    =?ISO-8859-1?Q?Martin_J=F8rgensen?=, Feb 16, 2006, in forum: C Programming
    Replies:
    185
    Views:
    3,335
    those who know me have no need of my name
    Apr 3, 2006
  4. =?ISO-8859-1?Q?Martin_J=F8rgensen?=

    difference between scanf("%i") and scanf("%d") ??? perhaps bug inVS2005?

    =?ISO-8859-1?Q?Martin_J=F8rgensen?=, Apr 26, 2006, in forum: C Programming
    Replies:
    18
    Views:
    656
    Richard Bos
    May 2, 2006
  5. Replies:
    4
    Views:
    834
    Mike Treseler
    Dec 16, 2008
Loading...

Share This Page