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

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

数据可以是整数"%d" ,双"%f" ,字符串"%s" ,unsigned int "%u" ……

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

示例1的问题

OP可以问:

  • 扫描输入数据
  • 数据应该是整数,因此不允许11aaaaaa44 ,…输入。 只允许整数后跟一个空格(请参考isspace() )
  • 其他条件可能存在于以下问题中:输入整数应>3<15

示例2的问题

OP可以问:

  • 扫描输入数据
  • 数据应该加倍,所以11.2aaaaaa44.3 ,……输入是不允许的。 只允许双后跟一个空格(请参考isspace() )
  • 其他条件可能存在于以下问题中:输入double应>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 。 如果我们检查下一个charachter是一个空白区域(使用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