如何防止用户输入比 C 中要求的更多或更少的输入?

How to prevent user from entering more or less input than required in C?

我希望用户只输入两个整数,不能多于二,也不能少于二。此外,在无效输入时,我希望打印错误并提示用户再次输入这两个整数。用户应输入两个由 space 分隔的整数,而不是换行符。因此,例如:
1) 有效输入为:1 2
2) 无效输入:1
3) 无效输入:1 2 3

我试过以下两种方法:

#include<stdio.h>

int main(){

   int first;
   int second;
   printf("Enter input:\n");
   int returnValue = scanf("%d %d", &first, &second);
   while(returnValue != 2){
        printf("Invalid input. Please enter again: \n");
        returnValue = scanf("%d %d", &first, &second);
   }
   printf("First: %d Second: %d\n", first, second);
   return 0;
}

在涉及 scanf 的第一种方法中,我无法阻止用户在换行符上输入每个整数。我也不能将输入限制为仅 2 个数字。也就是说,如果用户输入超过 2 个整数,则程序将接受前 2 个整数并忽略第三个。我想在那种情况下打印错误。

我的其他方法涉及 fgets 和 sscanf:

#include<stdio.h>

int main(){

  int first;
  int second;
  printf("Enter input:\n");
  char line[20];
  fgets(line, sizeof(line), stdin);
  int returnValue = sscanf(line, "%d %d", &first, &second);

  while(returnValue != 2){
     printf("Invalid input. Please enter again: \n");
     fgets(line, sizeof(line), stdin);
     returnValue = sscanf(line, "%d %d", &first, &second);
  }

  printf("First: %d Second: %d\n", first, second);
  return 0;
 } 

在这种方法中,如果用户在仅输入一个整数后按回车键,我可以打印错误。但我无法将输入限制为仅 2 个数字。也就是说,如果用户输入超过 2 个整数,则程序将接受前 2 个整数并忽略第三个。我想在那种情况下打印错误。

所以我的问题是,我的要求是否可以通过修改第一种方法和第二种方法来实现?

谢谢。

为防止请求 char * 时出现问题,您可以使用 regular expression。 如果你不是被迫二合一scanf你可以使用这个功能:

int secure_input(int max, int min) {
    int choice,buffer;
    do {
        choice = -1;//initialize in a values not included among min and max
        scanf("%d", &choice);
        while ((buffer =  getchar()) != '\n' ? buffer != EOF : false); // empty the buffer to avoid infinite loop
    } while (choice > max ? true : choice < min); 
    return choice;
}

在您的主函数中,您只需像这样调用该函数:

first = secure_input(2;1);

一种解决方案是在两次 %d 转换后使用 %n 转换规范。 %n 转换规范不匹配任何字符,而是将读取到此时的字符数存储在格式字符串中。所以,在通话中:

sscanf(line, "%d %d %n", &first, &second, &bufPos);

如果到达第二个%d,则bufPos将保存在line中读取的最后一个字符之后的字符索引。由于 %n 之前有一个 space,因此在将索引值存储到 bufPos 之前,将读取并跳过零个或多个 white-space 个字符。因此,在有效输入后,bufPos 将指示 [=22=] 终止符。如果在此索引处的 line 中找到任何其他字符,则说明输入中存在无关字符。

这是您的第二个代码示例的修改版本。 fgets()读取一行输入后,sscanf()用于扫描字符串。如果少于 2 个匹配项,或者如果 line[bufPos] 不是 '[=27=]',则 badInput 设置为 true。输入循环是一个 do 循环,执行一次,只要 badInputtrue.

就会继续执行
#include <stdio.h>
#include <stdlib.h>                 // for exit()
#include <stdbool.h>                // for bool type

#define BUF_SIZE  100

int main(void)
{
    int first;
    int second;
    char line[BUF_SIZE];
    int returnValue;
    int bufPos;
    bool badInput = false;

    do {
        if (badInput) {
            printf("Invalid input. Please enter again: ");
            badInput = false;
        } else {
            printf("Enter input: ");
        }
        if (fgets(line, sizeof(line), stdin) == NULL) {
            perror("Error in fgets()");
            exit(EXIT_FAILURE);
        }

        returnValue = sscanf(line, "%d %d %n", &first, &second, &bufPos);

        if (returnValue < 2 || line[bufPos] != '[=11=]') {
            badInput = true;
        }

    } while (badInput);

    printf("First: %d Second: %d\n", first, second);

    return 0;
}

交互示例:

Enter input: 1
Invalid input. Please enter again: 1 2 3
Invalid input. Please enter again: 
Invalid input. Please enter again: 1 2
First: 1 Second: 2

与其他答案不同,您还可以使用 strtok() 解析输入,然后检查找到了多少个数字。这种方法很复杂,但它确实提供了对问题的不同看法。

在你的 while() 循环中,你可以检查从 fgets() 中找到了多少个间隔数字,然后如果只找到 2,那么你可以 break 出来的循环。否则,继续寻找。一旦退出循环,您就可以从最近的输入读取中 sscanf() 两个整数。您还可以使用 strtol() 检查整数是否有效。

注意: strtok() 是可重入的,它确实修改了它解析的字符串。所以在这种情况下,您可能需要在某处创建它的副本。您可以使用 strdup() or malloc() 来执行此操作。

这里有一些示例代码可以说明这一点:

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

#define LINESIZE 20
#define BASE 10

int main(void) {
    char line[LINESIZE];
    const int n = LINESIZE;

    char *number, *copy, *endptr;
    const char *delim = " ";

    int first, second, check, invalidnum;
    size_t slen, count;

    while (1) {
        printf("Enter input: ");
        if (fgets(line, n, stdin) == NULL) {
            printf("Error reading buffer from fgets()\n");
            exit(EXIT_FAILURE);
        }

        slen = strlen(line);
        if (slen > 0 && line[slen-1] == '\n') {
            line[slen-1] = '[=10=]';
        } else {
            printf("Buffer overflow detected\n");
            exit(EXIT_FAILURE);
        }

        copy = strdup(line);

        count = 0;
        invalidnum = 0;
        number = strtok(copy, delim);
        while (number != NULL) {
            check = strtol(number, &endptr, BASE);
            if (endptr == number || check == 0) {
                invalidnum = 1;
                break;
            }
            count++;
            number = strtok(NULL, delim);
        }
        free(copy);
        copy = NULL;

        if (count != 2 || invalidnum) {
            printf("Invalid input\n\n");
        } else {
            break;
        }
    }

    if (sscanf(line, "%d %d", &first, &second) != 2) {
        printf("Unexpected error from sscanf()\n");
        exit(EXIT_FAILURE);
    }
    printf("first = %d, second = %d\n", first, second);

    return 0;
}

这只是解决您的问题的另一种方法。就简单性而言,@David Bowling 有更好的主意,我建议使用他的。