用于读取输入数据并检查其有效性的通用宏
我在Stack Overflow上看到许多类似的问题被重复,它们与从stdin读取一个输入数据项并检查其有效性有关。
数据可以是整数"%d"
,双"%f"
,字符串"%s"
,unsigned int "%u"
……
我想开发一个可以用于大多数问题的通用宏。
示例1的问题
OP可以问:
- 扫描输入数据
- 数据应该是整数,因此不允许
11a
,aaa
,aa44
,…输入。 只允许整数后跟一个空格(请参考isspace() ) - 其他条件可能存在于以下问题中:输入整数应
>3
且<15
示例2的问题
OP可以问:
- 扫描输入数据
- 数据应该加倍,所以
11.2a
,aaa
,aa44.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