读取输入数据并检查其有效性的通用宏
我在 Stack Overflow 上看到很多类似的问题重复出现,它们与从 stdin 读取一个输入数据项并检查其有效性有关.
I saw on Stack Overflow that many similar questions are repeated and they are related to the reading of one input data item from stdin and check its validity.
数据可以是整数"%d"
, double "%f"
, string "%s"
, unsigned int %u"
...
The data could be integer "%d"
, double "%f"
, string "%s"
, unsigned int "%u"
...
我想开发一个通用宏,可以用于大部分这些问题.
And I want to develop a common macro which could be used for the majority of these questions.
问题示例 1
OP 可能会问:
- 扫描输入数据
- 数据应该是整数,所以
11a
,aaa
,aa44
,... 输入是不允许的.只允许后跟空格的整数(参考 isspace())李> - 问题中可能存在其他条件,例如:输入整数应为
>3
和<15
- scan the input data
- the data should be integer so
11a
,aaa
,aa44
,... inputs are not allowed. Only integer followed with a white space (refer to isspace()) is allowed - other conditions could be present in the question like: input integer should be
>3
and<15
问题示例 2
OP 可能会问:
- 扫描输入数据
- 数据应该是双精度的,所以
11.2a
,aaa
,aa44.3
,... 输入是不允许的.只允许双后跟一个空格(参考 isspace())李> - 问题中可能存在其他条件,例如:输入双精度应为
>3.2
和<15.0
- scan the input data
- the data should be double so
11.2a
,aaa
,aa44.3
,... inputs are not allowed. Only double followed with a white space (refer to isspace()) is allowed - other conditions could be present in the question like: input double should be
>3.2
and<15.0
是否可以开发一个通用的宏,例如
Is it possible to develop a common macro like
#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))
宏应扫描数据,如果以下条件之一不正确,则向用户打印一条消息,要求他再次输入.并重复此操作,直到用户输入有效数据?
The macro should scan the data and IF one of the following criteria is not true then print a message to the user asking him to enter again. and repeat that until the user enters valid data?
标准:
- 输入数据类型应与格式指示的相同
- 输入数据后应为 isspace() 中指示的空格/li>
- 输入数据应符合条件
COND
- input data type should be the same indicated by format
- input data should be followed by white space indicated in the isspace()
- input data should be valid the condition
COND
推荐答案
这个问题的快速答案是
A quick answer for this question will be
#define SCAN_ONEENTRY_WITHCHECK(FORM,X,COND)
while(scanf(" "FORM, X)<1 || !(COND))
printf("Invalid input, enter again: ");
在代码中调用上述宏的方式如下
calling the above macro in the code with the following way
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)
捕获.所以如果用户输入这样的输入,我们必须添加一些清理标准输入的东西.添加 scanf("%*[^
]")
可能是一个解决方案.上面的代码将是
but the above answer is wrong because if the user enter for example aaa
as input then the above code will result an infinite loop because the stdin is not cleaned because the aaa
input is not catched by the scanf(" %d", &a)
. So we have to add something that clean the stdin if the user enter a such input. adding scanf("%*[^
]")
could be a solution for that. and the above code will be
int a;
while(scanf(" %d", &a)<1 || !(a>3 && a<15)) {
scanf("%*[^
]"); // clean stdin
printf("Invalid input, enter again: ");
}
但上面的代码仍然包含一个限制.例如,如果用户输入了一个有效的整数 (20
),并且该整数不遵守条件 (a>3 && a<15)
.在这种情况下,我们不必清理标准输入,因为它已经被清理了,否则用户将被要求输入数据 2 次.这个问题可以通过以下解决方案解决:
but the above code still contains a limitation. For example If the user enter a valid integer (20
) and the integr does not respect the condition (a>3 && a<15)
. In this case we do not have to clean the stdin because it's already cleaned otherwise the user will be asked for input data for 2 times . tis problem coud solved with the following solution:
int a;
int c;
while((c=(scanf(" %d", &a)<1)) || !(a>3 && a<15)) {
if (c) scanf("%*[^
]"); // clean stdin
printf("Invalid input, enter again: ");
}
上面的代码不遵守标准输入数据后面应该有空格
例如,如果用户输入 10abc
作为输入,那么上面的代码将捕获 10 作为输入整数,它将清除其余的abc
.如果我们检查下一个字符是否为空格(使用 isspace()
函数),则可以解决此问题
The above code does not respect the criteria input data should be followed by white space
for example if the user enter 10abc
as input then the above code will catch 10 as input integer and it will clean the rest abc
. this problem could be solved if we check that the next charachter is a white space (with isspace()
function) nothing else
int a;
char tmp;
int c;
while((c=(scanf(" %d%c", &a, &tmp)!=2 || !isspace(tmp))) || !(a>3 && a<15)) {
if (c) scanf("%*[^
]"); // clean stdin
printf("Invalid input, enter again: ");
}
现在如果我们想回到宏.宏代码将是:
And now if we want to go back to the macro. the macro code will be:
#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("%*[^
]");
printf("Invalid input, please enter again: ");
}
} while(0)
在宏中添加 do {...} while(0)
可以通过 链接
Adding do {...} while(0)
in the macro could be explained by this link
上面的宏可以写成另一种方式
The above macro could be writen in another way
#define SCAN_ONEENTRY_WITHCHECK(FORM,X,COND)
do {
char tmp;
while(((scanf(" "FORM"%c",X,&tmp)!=2 || !isspace(tmp)) && !scanf("%*[^
]"))
|| !(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
", decision);
printf("Input unsigned double: ");
SCAN_ONEENTRY_WITHCHECK("%lf",&q, (q == (unsigned int) q));
printf("You have entered good input : %lf
", q);
printf("Input name: ");
SCAN_ONEENTRY_WITHCHECK("%s",buf, (strcmp(buf,"kallel")==0));
printf("You have entered good input : %s
", 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
", 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
相关文章