51

我有一个小的 C 程序,它只从标准输入读取数字,每个循环周期一个。如果用户输入了一些 NaN,则应将错误打印到控制台并再次返回输入提示。在输入“0”时,循环应该结束,给定的正/负值的数量应该打印到控制台。这是程序:

#include <stdio.h>

int main()
{
    int number, p = 0, n = 0;

    while (1) {
        printf("-> ");
        if (scanf("%d", &number) == 0) {
            printf("Err...\n");
            continue;
        }
        
        if (number > 0) p++;
        else if (number < 0) n++;
        else break; /* 0 given */
    }

    printf("Read %d positive and %d negative numbers\n", p, n);
    return 0;
}

我的问题是,在输入一些非数字(如“a”)时,这会导致无限循环一遍又一遍地写入“-> Err ...”。我想这是一个 scanf() 问题,我知道这个函数可以被一个更安全的函数替换,但这个例子是为初学者准备的,只知道 printf/scanf、if-else 和循环。

我已经阅读了这个问题的答案,scanf()跳过了 C 中的所有其他while循环并浏览了其他问题,但没有什么能真正回答这个具体问题。

4

16 回答 16

42

scanf只消耗与格式字符串匹配的输入,返回消耗的字符数。任何与格式字符串不匹配的字符都会导致它停止扫描并将无效字符留在缓冲区中。正如其他人所说,您仍然需要在继续之前将无效字符从缓冲区中清除。这是一个非常肮脏的修复,但它会从输出中删除有问题的字符。

char c = '0';
if (scanf("%d", &number) == 0) {
  printf("Err. . .\n");
  do {
    c = getchar();
  }
  while (!isdigit(c));
  ungetc(c, stdin);
  //consume non-numeric chars from buffer
}

编辑:修复代码以一次性删除所有非数字字符。不再为每个非数字字符打印多个“错误”。

是对scanf的一个很好的概述。

于 2009-11-11T15:47:39.280 回答
8

scanf()下一次将“ a”留在输入缓冲区中。getline()无论如何,您可能应该使用读取一行,然后用strtol()或类似的方式解析它。

(是的,getline()是 GNU 特定的,而不是 POSIX。那又怎样?这个问题被标记为“gcc”和“linux”。 getline()这也是阅读一行文本的唯一明智选择,除非你想手动完成所有操作。)

于 2009-11-11T15:41:30.817 回答
8

我认为您只需要在继续循环之前刷新缓冲区即可。像这样的东西可能会完成这项工作,尽管我无法测试我从这里写的内容:

int c;
while((c = getchar()) != '\n' && c != EOF);
于 2009-11-11T15:42:54.937 回答
4

而不是使用scanf()and 必须处理具有无效字符的缓冲区,使用fgets()and sscanf()

/* ... */
    printf("0 to quit -> ");
    fflush(stdout);
    while (fgets(buf, sizeof buf, stdin)) {
      if (sscanf(buf, "%d", &number) != 1) {
        fprintf(stderr, "Err...\n");
      } else {
        work(number);
      }
      printf("0 to quit -> ");
      fflush(stdout);
    }
/* ... */
于 2009-11-11T16:00:53.620 回答
4

由于scanf其他答案指出的问题,您应该真正考虑使用另一种方法。scanf对于任何严肃的输入阅读和处理,我总是觉得太有限了。最好先用 with 读取整行,fgets然后使用 and 之类的函数处理它们strtokstrtol顺便说一句,它会正确解析整数并准确告诉您无效字符的开始位置)。

于 2009-11-11T15:53:15.440 回答
3

我有类似的问题。我只使用scanf解决了。

Input "abc123<Enter>"看看它是如何工作的。

#include <stdio.h>
int n, num_ok;
char c;
main() {
    while (1) {
        printf("Input Number: ");
        num_ok = scanf("%d", &n);
        if (num_ok != 1) {
            scanf("%c", &c);
            printf("That wasn't a number: %c\n", c);
        } else {
            printf("The number is: %d\n", n);
        }
    }
}
于 2012-09-14T13:25:22.120 回答
1

在某些平台(尤其是 Windows 和 Linux)上,您可以使用fflush(stdin);

#include <stdio.h>

int main(void)
{
  int number, p = 0, n = 0;

  while (1) {
    printf("-> ");
    if (scanf("%d", &number) == 0) {
        fflush(stdin);
        printf("Err...\n");
        continue;
    }
    fflush(stdin);
    if (number > 0) p++;
    else if (number < 0) n++;
    else break; /* 0 given */
  }

  printf("Read %d positive and %d negative numbers\n", p, n);
  return 0;
}
于 2012-10-25T16:25:57.717 回答
1

解决方法:需要fflush(stdin);0scanf.

原因:当遇到错误时,它似乎将输入字符留在缓冲区中,因此每次scanf调用它只是不断尝试处理无效字符,但从不将其从缓冲区中删除。当您调用fflush时,输入缓冲区(stdin)将被清除,因此将不再重复处理无效字符。

您修改了程序:以下是您的程序修改了所需的更改。

#include <stdio.h>

int main()
{
    int number, p = 0, n = 0;

    while (1) {
        printf("-> ");
        if (scanf("%d", &number) == 0) {
            fflush(stdin);
            printf("Err...\n");
            continue;
        }

        if (number > 0) p++;
        else if (number < 0) n++;
        else break; /* 0 given */
    }

    printf("Read %d positive and %d negative numbers\n", p, n);
    return 0;
}
于 2019-07-26T17:10:08.570 回答
0

我有同样的问题,我找到了一个有点hacky的解决方案。我fgets()用来读取输入,然后将其提供给sscanf(). 对于无限循环问题,这不是一个糟糕的解决方案,并且通过一个简单的 for 循环,我告诉 C 搜索任何非数字字符。下面的代码不允许像123abc.

#include <stdio.h>
#include <ctype.h>
#include <string.h>

int main(int argc, const char * argv[]) {

    char line[10];
    int loop, arrayLength, number, nan;
    arrayLength = sizeof(line) / sizeof(char);
    do {
        nan = 0;
        printf("Please enter a number:\n");
        fgets(line, arrayLength, stdin);
        for(loop = 0; loop < arrayLength; loop++) { // search for any none numeric charcter inisde the line array
            if(line[loop] == '\n') { // stop the search if there is a carrage return
                break;
            }
            if((line[0] == '-' || line[0] == '+') && loop == 0) { // Exculude the sign charcters infront of numbers so the program can accept both negative and positive numbers
                continue;
            }
            if(!isdigit(line[loop])) { // if there is a none numeric character then add one to nan and break the loop
                nan++;
                break;
            }
        }
    } while(nan || strlen(line) == 1); // check if there is any NaN or the user has just hit enter
    sscanf(line, "%d", &number);
    printf("You enterd number %d\n", number);
    return 0;
}
于 2013-12-30T08:38:03.703 回答
0

为了部分解决您的问题,我只是在 scanf 之后添加了这一行:

fgetc(stdin); /* to delete '\n' character */

下面,您的代码与该行:

#include <stdio.h>

int main()
{
    int number, p = 0, n = 0;

    while (1) {
        printf("-> ");
        if (scanf("%d", &number) == 0) {
            fgetc(stdin); /* to delete '\n' character */
            printf("Err...\n");
            continue;
        }

        if (number > 0) p++;
        else if (number < 0) n++;
        else break; /* 0 given */
    }

    printf("Read %d positive and %d negative numbers\n", p, n);
    return 0;
}

但如果输入多个字符,程序会一个接一个地继续,直到出现“\n”。

所以我在这里找到了一个解决方案:How to limit input length with scanf

您可以使用此行:

int c;
while ((c = fgetc(stdin)) != '\n' && c != EOF);
于 2019-05-12T01:19:40.733 回答
0

输入非数字时会发生错误,并且非数字仍保留在输入缓冲区中。你应该跳过它。同样,即使是这种符号组合,例如1a,最初也会被读取为数字 1,我认为你也应该跳过这样的输入。

该程序可以如下所示。

#include <stdio.h>
#include <ctype.h>

int main(void) 
{
    int p = 0, n = 0;

    while (1)
    {
        char c;
        int number;
        int success;

        printf("-> ");

        success = scanf("%d%c", &number, &c);

        if ( success != EOF )
        {
            success = success == 2 && isspace( ( unsigned char )c );
        }

        if ( ( success == EOF ) || ( success && number == 0 ) ) break;

        if ( !success )
        {
            scanf("%*[^ \t\n]");
            clearerr(stdin);
        }
        else if ( number > 0 )
        {
            ++p;
        }
        else if ( number < n )
        {
            ++n;
        }
    }

    printf( "\nRead %d positive and %d negative numbers\n", p, n );

    return 0;
}

程序输出可能看起来像

-> 1
-> -1
-> 2
-> -2
-> 0a
-> -0a
-> a0
-> -a0
-> 3
-> -3
-> 0

Read 3 positive and 3 negative numbers
于 2017-02-09T19:55:06.673 回答
0
// all you need is to clear the buffer!

#include <stdio.h>

int main()
{
    int number, p = 0, n = 0;
    char clearBuf[256]; //JG:
    while (1) {
        printf("-> ");
        if (scanf("%d", &number) == 0) {
            fgets(stdin, 256, clearBuf); //JG:
            printf("Err...\n");
            continue;
        }

        if (number > 0) p++;
        else if (number < 0) n++;
        else break; /* 0 given */
    }

    printf("Read %d positive and %d negative numbers\n", p, n);
    return 0;
}
于 2020-07-03T13:48:55.677 回答
0

尝试使用这个:

if (scanf("%d", &number) == 0) {
        printf("Err...\n");
        break;
    }

这对我来说很好......试试这个.. continue语句不合适,因为Err..应该只执行一次。所以,试试我测试过的break ……这对你来说很好……我测试过……

于 2016-10-07T05:34:49.560 回答
-1

在扫描之前刷新输入缓冲区:

while(getchar() != EOF) continue;
if (scanf("%d", &number) == 0) {
    ...

我打算建议fflush(stdin),但显然这会导致未定义的行为

作为对您的评论的回应,如果您希望显示提示,您必须刷新输出缓冲区。默认情况下,仅当您打印换行符时才会发生这种情况。喜欢:

while (1) {
    printf("-> ");
    fflush(stdout);
    while(getchar() != EOF) continue;
    if (scanf("%d", &number) == 0) {
    ...
于 2009-11-11T15:43:17.223 回答
-1

嗨,我知道这是一个旧线程,但我刚刚完成了一项学校作业,遇到了同样的问题。我的解决方案是我使用 gets() 来获取 scanf() 留下的内容。

这是稍微重写的 OP 代码;可能对他没有用,但也许它会帮助那里的其他人。

#include <stdio.h>

    int main()
    {
        int number, p = 0, n = 0;
        char unwantedCharacters[40];  //created array to catch unwanted input
        unwantedCharacters[0] = 0;    //initialzed first byte of array to zero

        while (1)
        {
            printf("-> ");
            scanf("%d", &number);
            gets(unwantedCharacters);        //collect what scanf() wouldn't from the input stream
            if (unwantedCharacters[0] == 0)  //if unwantedCharacters array is empty (the user's input is valid)
            {
                if (number > 0) p++;
                else if (number < 0) n++;
                else break; /* 0 given */
            }
            else
                printf("Err...\n");
        }
        printf("Read %d positive and %d negative numbers\n", p, n);
        return 0;
    }
于 2015-03-16T13:01:45.020 回答
-1

晚上好。我最近遇到了同样的问题,我找到了一个可能对很多人有帮助的解决方案。好吧,实际上函数“scanf”在内存中留下了一个缓冲区……这就是导致无限循环的原因。因此,如果您的初始 scanf 包含“null”值,您实际上必须将此缓冲区“存储”到另一个变量。这就是我的意思:

#include <stdio.h>
int n;
char c[5];
main() {
    while (1) {
        printf("Input Number: ");
        if (scanf("%d", &n)==0) {  //if you type char scanf gets null value
            scanf("%s", &c);      //the abovementioned char stored in 'c'
            printf("That wasn't a number: %s\n", c);
        }
        else printf("The number is: %d\n", n);
    }
}
于 2016-12-14T21:56:50.577 回答