1

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

数据可以是 integer "%d"、 double "%f"、 string "%s"、 unsigned int "%u"...

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

问题示例 1

OP可能会问:

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

问题示例 2

OP可能会问:

  • 扫描输入数据
  • 数据应该是双倍的,所以11.2a, aaa, aa44.3,... 不允许输入。只允许double 后跟一个空格(请参阅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
4

1 回答 1

1

这个问题的快速答案是

#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作为输入,那么上面的代码将导致无限循环,因为标准输入没有被清理,因为aaa输入没有被scanf(" %d", &a). 所以如果用户输入这样的输入,我们必须添加一些清理标准输入的东西。添加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)。在这种情况下,我们不必清理标准输入,因为它已经被清理了,否则用户将被要求输入数据 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 : %d\n", decision);

    printf("Input unsigned double: ");
    SCAN_ONEENTRY_WITHCHECK("%lf",&q, (q == (unsigned int) q));
    printf("You have entered good input : %lf\n", q);

    printf("Input name: ");
    SCAN_ONEENTRY_WITHCHECK("%s",buf, (strcmp(buf,"kallel")==0));
    printf("You have entered good input : %s\n", 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 : %d\n", 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
于 2013-04-03T16:31:38.817 回答