【问题标题】:Good way to flush scanf buffer when invalid entry entered输入无效条目时刷新scanf缓冲区的好方法
【发布时间】:2014-07-27 05:03:32
【问题描述】:

我一直在想办法清除scanf 函数中的错误条目,以允许循环提示完成它们的工作。

我在这里有一个函数调用来刷新输入。这可行,但如果我在要求int 时输入类似2q 的内容,它仍然存在问题。

void flushKeyBoard()
{
    int ch; //variable to read data into
    while((ch = getc(stdin)) != EOF && ch != '\n');
}

然后我会在另一个函数中有这样的东西:

printf("Enter a value: ");
check = scanf("%f", &b);

while(check == 0)
{
    printf("Invalid number entered. Please re-enter: ");
    check = scanf("%f, &b");
    flushKeyBoard();
}

有更好的想法吗?有人建议 fflush();但在这里使用它并不是真正的标准..

【问题讨论】:

  • 阅读 C-faq 问题 12.26a12.26b
  • 您现有的flushKeyBoard 看起来不错,您到底遇到了什么问题?
  • 是否要将 "2q" 视为有效的 int 并丢弃 "q" ,或者是否要丢弃整行,是您必须在代码中控制的内容正在做scanf。如果您想将“2q”视为错误(而不是读取“2”然后丢弃 q)您可以更新 flushKeyboard 以返回 bool 以指示它是否确实清除了某些内容。
  • scanf() 可以返回 EOF,以及 0 或 1。除 1 之外的任何其他值(在发布的代码中)都是错误的。所以代码应该检查 EOF,也许在 while 循环之后。

标签: c gcc scanf


【解决方案1】:

使用getchar(常见,容易理解)

int c;

while ((c = getchar()) != EOF && c != '\n'); /* <note the semicolon! */

if (c == EOF) {
    if (feof(stdin)) {
        /* Handle stdin EOF. */
    }
    else {
        /* Handle stdin error. */
    }
}

使用fgets(不太常见,不易理解)

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. */
}

使用带有scanf 的扫描集(可能不常见,易于理解)

/*
 * 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'.

使用getline(可能不常见,困难)

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);

当然,所有这些方法都假定您希望以不同于 \n 的方式处理发生在 EOF/错误上的事情,甚至可能所有这三种情况都是不同的情况。例如,通过将上述 sn-ps 之一放入自包含函数中,如果读取了 \n,您可能会返回 0,或者在 EOF/error 上返回 EOF,甚至在 \n 上返回 0EOF 在 EOF 上,1 在错误上。

注意事项:

  • getcharfgets 方法是 100% 跨平台的。我更喜欢 getchar 方法,因为它很简单。
  • scanset 方法的跨平台性较差,只是因为并非所有编译器都实现了扫描集(因此不符合 C99)。
  • getline 方法也不是很跨平台:它主要在 GNU/Linux 和其他一些 POSIX 操作系统上实现;这不包括此时的 Windows。一旦你有一些管理内存和使用指针的经验,你自己写一个并不难,但你最好使用前两种方法之一,因为编写getline的实现可能最终会使用@ 987654342@ 或 fgets 无论如何(fgetc(stdin)getchar() 的行为应该相同)。

【讨论】:

    【解决方案2】:

    你可以使用:

    fscanf(stdin, "%*[^\n]%*c");
    

    【讨论】:

      【解决方案3】:

      有几种方法可以做到这一点。实际上,您要做的是使用最多一个 \n 字符(或 EOF)的文本行。要正确执行此操作,您应该使用 sscanf(或 fscanf)。

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

      这使用正则表达式 [^\n]*\n(任意数量的非 \n 字符,后跟 \n)并消耗所有匹配的内容。

      编辑:因为我很笨,忘记了 scanf 正则表达式使用稍微不同的语法。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-05-23
        • 1970-01-01
        • 2015-08-10
        • 2016-01-21
        • 2015-03-07
        • 2021-11-19
        • 2014-03-13
        • 1970-01-01
        相关资源
        最近更新 更多