Good way to flush scanf buffer when invalid entry entered

前端 未结 3 600
轻奢々
轻奢々 2021-01-23 20:18

I have been thinking of ways to flush bad entries in scanf functions to allow for loop prompts to do their job.

I have a function call here that flushes th

相关标签:
3条回答
  • 2021-01-23 20:42

    Using getchar (common, easily comprehensible)

    int c;
    
    while ((c = getchar()) != EOF && c != '\n'); /* <note the semicolon! */
    
    if (c == EOF) {
        if (feof(stdin)) {
            /* Handle stdin EOF. */
        }
        else {
            /* Handle stdin error. */
        }
    }
    

    Using fgets (less common, less comprehensible)

    char buf[8];
    
    while (fgets(buf, sizeof buf, stdin) != NULL) {
        size_t len = strlen(buf);
    
        /*
         * Exit the loop if either EOF was encountered before '\n', or
         * if '\n' is detected.
         */
        if (len + 1 != sizeof(buf) || memchr(buf, '\n', len))
            break;
    }
        
    if (feof(stdin)) {
        /* Handle stdin EOF. */
    }
    else {
        /* Handle stdin error. */
    }
    

    Using a scanset with scanf (likely uncommon, easily comprehensible)

    /*
     * Combining the scanset with assignment suppression (the '*' before the
     * scanset) will return EOF on EOF/error and 0 if '\n' was read.
     */
    if (scanf("%*[^\n]") == EOF) {
        if (feof(stdin)) {
            // Handle stdin EOF.
        }
        else {
            // Handle stdin error.
        }
    }
    getchar(); // Flush the '\n'.
    

    Using getline (likely uncommon, difficult)

    char *buf = NULL;
    size_t bufsize = 0;
    ssize_t len;
    
    /* getline() will stop reading on '\n' or EOF. */
    len = getline(&buf, &bufsize, stdin);
    
    /* No bytes read and EOF encountered, or there was an error. */
    if (len == -1) {
        if (feof(stdin)) {
            /* Handle stdin EOF. */
        }
        else if (ferror(stdin)) {
            /* Handle stdin error. */
        }
        else {
            /* Handle errno error, if desired. */
        }
        /*
         * The value of "buf" is indeterminate here, so you likely
         * just want to return from the function/program at this point
         * rather than continuing and potentially freeing an invalid
         * buffer.
         */
    }
    free(buf);
    

    Of course, all of these methods assume you want to handle things that happen on EOF/error differently than with \n, perhaps even all three being separate cases. For example, by placing one of the above snippets into a self-contained function, you might return 0 if \n was read or EOF on EOF/error, or even 0 on \n, EOF on EOF, and 1 on error.

    Things worth noting:

    • The getchar and fgets methods are 100% cross-platform. I prefer the getchar method for its simplicity.
    • The scanset method is less cross-platform only because not all compilers implement scansets (and are therefore not C99-compliant).
    • The getline method is also not quite cross-platform: it's primarily implemented on GNU/Linux and on some other POSIX operating systems; this does not include Windows at this time. It isn't terribly difficult to write one yourself once you have some experience managing memory and working with pointers, but you'd be better off using one of the first two methods since writing an implementation of getline would likely end up using fgetc or fgets anyway (fgetc(stdin) and getchar() should behave identically).
    0 讨论(0)
  • 2021-01-23 20:55

    You can use:

    fscanf(stdin, "%*[^\n]%*c");
    
    0 讨论(0)
  • 2021-01-23 21:07

    There's a few ways of doing this. Effectively, what you're trying to do is consume a line of text up to a \n character (or an EOF.) To do this properly, you should use sscanf (or fscanf.)

    sscanf("%*[^\n]\n");
    

    This uses the regular expression [^\n]*\n (Any number of characters that are NOT \n, followed by a \n) and consumes everything matching that.

    Edit: Because I'm dumb and forgot that scanf regex's use a slightly different syntax.

    0 讨论(0)
提交回复
热议问题