【问题标题】:Is there a way of limiting scanf in C?有没有办法限制C中的scanf?
【发布时间】:2020-11-29 22:33:02
【问题描述】:

我正在尝试为链表的使用编写正确的控制台应用程序,因此我需要在无限循环中扫描多个命令并根据 switch case 选项执行某些操作。所以我为此使用scanf,但问题是当下一行不包含数字时,它会循环并开始打印甚至没有默认值。

`while(1)
    {
            printf("Enter a number of a command.\n");
            scanf("%d",&command);
            switch(command)
            {
            case -1:
            ....
            default:
                  printf("Reenter command.\n");
                  break;
            }
    }

似乎当我读取无限量的数据堆栈时,它被重写了。我知道我必须限制阅读符号的数量,但不明白如何以正确的方式做到这一点。

在 Ubuntu 18.04.2 LTS 上使用 gcc 版本 5.4.0 (GCC)、c99

【问题讨论】:

  • 我会先检查 scanf 的结果,然后再考虑它实际(假设)扫描过的内容,而不是盲目地假设它只是“有效”。它有一个记录的返回值是有原因的。或许使用它,当事情变糟时,通过换行符或EOF(以先到者为准)丢弃输入流数据。
  • 你要么想要一个scanf 模式来获取数字,但也保证它会占用换行符,或者,执行fgets [强制整行],然后使用sscanf缓冲。然后,如果它不是一个数字,则很容易重新循环 [无需处理类似:not_a_number 23] 的有趣行。您希望重新循环在第一行抛出任何 [bad] 并强制读取一个全新的行。IMO,fgets 然后strtol 是更好的选择。
  • scanf 看到无法与当前使用的格式匹配的输入时,该输入未被读取。因此,在您的示例中,如果 scanf 无法将输入与您的格式匹配,则循环返回以使用相同格式重试不太可能产生不同的结果。
  • 您刚刚发现为什么使用scanf() 读取输入是一个坏主意。使用fgets()getline() 之类的内容逐行读取输入,然后自己解析输入数据要可靠得多。至少这种方式意外输入不会像scanf() 那样让您的输入流处于未知状态...
  • 您可能想阅读以下内容:A beginners' guide away from scanf()

标签: c scanf


【解决方案1】:

我没有足够的声誉来发表评论,但这可能是您正在寻找的。还要尝试更具描述性。我已经粘贴了您的代码,我能发现的唯一问题是,当您按 Enter 而不插入数字(即字母)时,它会跳过。这应该可以解决它:

int readInt(const char *message, int min, int max){
    int num, control;
    do{
        printf("%s (%d a %d) :", message, min, max);
        control = scanf ("%d", &num);
        cleanBufferStdin();  
        if (control == 0)
        {
            printf("You should enter a number \n");
        }
        else{
            if(num<min || num>max)
            {
                printf("Number is invalid.\n");
            }
        }
    }  
    while(num<min || num>max || control ==0);

    return num;
}
void cleanBufferStdin(void)
{
    char chr;
    do
    {
        chr = getchar();
    }
    while (chr != '\n' && chr != EOF);
}

我编写了更多代码,并在对您的问题的另一种解释中(这个不仅检测您是否刚刚按下回车,而且如果您没有输入整数,我没有检查负数是否有效)我使用了这个函数:

//DONT FORGET TO #DEFINE WRONG_REQUEST_MACRO "SOME MESSAGE"
void readString(const char message*, char arrayChars *, int maxChars){
    int stringSize;
    unsigned char flag=0;
    do{
        flag =0;
        printf("%s", message);
        fgets(arrayChars, maxChars, stdin);
        
        
        stringSize = strlen(arrayChars);
        if (stringSize == 1){
            printf("[INFO]Empty request. You just pressed ENTER.\n"); 
            flag=1;
        }
        if (atoi(arrayChars)==0&&arrayChars[0]!=0){
            printf("[INFO]You didn't enter a number.\n");
            flag=1;

        }
    } while (flag == 1);

    if (arrayChars[stringSize - 1] != '\n'){
        clearBuffer(); 
    }else{
        arrayChars[stringSize - 1] = '\0'; 
    }

    while (strchr(arrayChars, '\'') != NULL || strchr(arrayChars, '?') != NULL || strchr(arrayChars, '*') != NULL || strchr(arrayChars, '\"') != NULL){
        printf("%s ' %s '", WRONG_REQUEST_MACRO, arrayChars);
        break;
    }
}

应该像这样使用

int command;
char message[20];//yes this could be used with a char pointer but lets assume op doesnt know how to allocate memory or work with char pointers it wouldn't change that much but if he does know how to do it he will promptly change

readString("something something i suppose\n",message,20); 
command=atoi(message);

Welp 最后一个,虽然它充满了调试“重复”应该可以工作

【讨论】:

  • int readInt(char message[MAX_STRING], int min, int max){ -> int readInt(const char const *message, int min, int max){ 可能会更好
  • if (atoi(arrayChars)==0&amp;&amp;arrayChars[0]!=0){ 行中,您可能希望将其更改为else if。甚至更好:您可能希望将两个flag = 1 行更改为continue 语句并将do...while 循环更改为无限循环(for(;;)while(1))。但是,在这种情况下,您必须在循环末尾添加 break 语句。这样,您将不再需要变量flag。在这种情况下,使用goto 标签而不是循环可能是更清洁的解决方案,但some people say that goto should not be used if possible
  • if (atoi(arrayChars)==0&amp;&amp;arrayChars[0]!=0){ 行中,表达式arrayChars[0]!=0 是有意的吗?还是您的意思是'0' 而不是0,即字符代码?换句话说,您打算测试第一个字符是数字'0' 还是检查空终止字符?
  • control = scanf ("%d", &amp;num); --> 导致 control 的值为 0、1 或 EOF。此代码不处理 EOF 情况。
  • 我已经很晚了,明天我会详细检查你的 cmets 是的,代码写得非常快,因为我担心大量 cmets 会进来,但他们仍然这样做了
猜你喜欢
  • 1970-01-01
  • 2014-03-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多