读取输入数据并检查其有效性的通用宏

Common macro to read input data and check its validity

本文关键字:有效性 输入 数据 检查 读取      更新时间:2023-10-16

我在Stack Overflow上看到许多类似的问题重复出现,它们都与从stdin读取一个输入数据项并检查其有效性有关。

数据可以是整型"%d",双型"%f",字符串"%s",无符号整型"%u"

我想开发一个通用的宏,可以用于这些问题的多数

问题示例1

OP可以问:

    扫描输入数据
  • 数据应该是整数,所以11a, aaa, aa44,…不允许输入。只允许后跟空格的整数(参考isspace())
  • 其他条件可能出现在问题中,如:输入整数应该是>3<15

问题示例二

OP可以问:

    扫描输入数据
  • 数据应该是双的,所以11.2a, aaa, aa44.3,…不允许输入。只允许双引号后跟空格(参考isspace())
  • 其他条件可能出现在问题中,如:输入双精度应该是>3.2<15.0

是否可能开发一个像

这样的通用宏?
#define SCAN_ONEENTRY_WITHCHECK(FORM,X,COND)
// FORM: format of the input data like "%d", "%lf", "%s"
// X: address where to store the input data
// COND: condition to add in the check of the input data
....
// example of calling the macro in the main()
int a;
SCAN_ONEENTRY_WITHCHECK("%d", &a,(a>3 && a<15))

宏应该扫描数据,如果下列条件之一不为真,则打印一条消息给用户,要求他再次输入。并重复该操作,直到用户输入有效数据?

标准:

  • 输入数据类型应与格式
  • 相同
  • 输入数据后面应该有isspace()
  • 中指示的空白。
  • 输入数据应在COND
  • 条件下有效

这个问题的简单答案是

#define SCAN_ONEENTRY_WITHCHECK(FORM,X,COND)
   while(scanf(" "FORM, X)<1 || !(COND))
      printf("Invalid input, enter again: ");

在代码中以如下方式调用上述宏

int a; 
SCAN_ONEENTRY_WITHCHECK("%d", &a, (a>3 && a<15))

等价于

int a;
while(scanf(" %d", &a)<1 || !(a>3 && a<15))
    printf("Invalid input, enter again: ");

但是上面的答案是错误的,因为如果用户输入例如aaa作为输入,那么上面的代码将导致一个无限循环,因为没有清理stdin,因为aaa的输入没有被scanf(" %d", &a)捕获。因此,如果用户输入这样的输入,我们必须添加一些东西来清理stdin。添加scanf("%*[^n]")可能是一个解决方案。上面的代码是

int a;
while(scanf(" %d", &a)<1 || !(a>3 && a<15)) {
    scanf("%*[^n]"); // clean stdin
    printf("Invalid input, enter again: ");
}

,但上面的代码仍然包含一个限制。例如,如果用户输入一个合法的整数(20),并且该整数不符合条件(a>3 && a<15)。在这种情况下,我们不需要清理stdin,因为它已经被清理过了,否则用户将被要求输入数据2次。这个问题可以用以下方法解决:

int a;
int c;
while((c=(scanf(" %d", &a)<1)) || !(a>3 && a<15)) {
    if (c) scanf("%*[^n]"); // clean stdin
    printf("Invalid input, enter again: ");
}

上面的代码不尊重input data should be followed by white space标准,例如,如果用户输入10abc作为输入,那么上面的代码将捕获10作为输入整数,并将清除其余的abc。这个问题可以解决,如果我们检查下一个字符是空白(与isspace()函数)没有其他

int a;
char tmp;
int c;
while((c=(scanf(" %d%c", &a, &tmp)!=2 || !isspace(tmp))) || !(a>3 && a<15)) {
    if (c) scanf("%*[^n]"); // clean stdin
    printf("Invalid input, enter again: ");
}

现在如果我们想回到宏。宏代码将是:

#define SCAN_ONEENTRY_WITHCHECK(FORM,X,COND) 
do {
    char tmp;
    int c;
    while ((c=(scanf(" "FORM"%c", X, &tmp)!=2 || !isspace(tmp)))
            || !(COND)) {
        if (c) scanf("%*[^n]");
        printf("Invalid input, please enter again: ");
    }
} while(0)

在宏中添加do {...} while(0)可以通过这个链接进行解释

上面的宏可以用另一种方式编写

#define SCAN_ONEENTRY_WITHCHECK(FORM,X,COND) 
do {
    char tmp;
    while(((scanf(" "FORM"%c",X,&tmp)!=2 || !isspace(tmp)) && !scanf("%*[^n]"))
            || !(COND)) {
        printf("Invalid input, please enter again: ");
    }
} while(0)

宏使用示例:

int main()
{
    int decision;
    double q;
    char buf[32];
    printf("Input data, valid choice 1 or 0: ");
    SCAN_ONEENTRY_WITHCHECK("%d",&decision,(decision==0 || decision==1));
    printf("You have entered good input : %dn", decision);
    printf("Input unsigned double: ");
    SCAN_ONEENTRY_WITHCHECK("%lf",&q, (q == (unsigned int) q));
    printf("You have entered good input : %lfn", q);
    printf("Input name: ");
    SCAN_ONEENTRY_WITHCHECK("%s",buf, (strcmp(buf,"kallel")==0));
    printf("You have entered good input : %sn", buf);
    printf("Input data should be valid integer: ");
    SCAN_ONEENTRY_WITHCHECK("%d",&decision,1);
    // COND is 1 ==> we do not have any check in the input integer
    printf("You have entered good input : %dn", decision);
}
执行

$ ./test
Input data, valid choice 1 or 0: 4
Invalid input, please enter again: a4
Invalid input, please enter again: a1
Invalid input, please enter again: 1a
Invalid input, please enter again: 1
You have entered good input : 1
Input unsigned double: 2.3
Invalid input, please enter again: a.0a
Invalid input, please enter again: 2.0a
Invalid input, please enter again: a2.0
Invalid input, please enter again: 2.0
You have entered good input : 2.000000
Input name: an
Invalid input, please enter again: anyad
Invalid input, please enter again: adny
Invalid input, please enter again: any
You have entered good input : any
Input data should be valid integer: 2.5
Invalid input, please enter again: -454f
Invalid input, please enter again: -454
You have entered good input : -454