我在Stack Overflow上看到许多类似的问题被重复,它们与从 stdin 读取一个输入数据项有关,并检查其有效性。
数据可以是整数"%d"
,双"%f"
,字符串"%s"
,无符号整数"%u"
...
我想开发一个可以用于这些问题的多数的通用宏。
问题示例1
OP可以问:11a
,aaa
,aa44
,...输入。只允许整数后跟一个空格(参考isspace())>3
和<15
问题示例2
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))
宏应该扫描数据并且如果以下标准之一不正确,则向用户打印消息,要求他再次输入。并重复这一过程,直到用户输入有效数据?
标准:
COND
答案 0 :(得分: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
作为输入,则上面的代码将导致无限循环,因为未清除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)
可以通过此link
解释在宏中添加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