使用 scanf 读取整数的正确方法?
Proper way to read integer with scanf?
我想实现一个读取整数的函数,但这个函数应该是:
- 适应
\n
- 对
^D
(EOF
) 的鲁棒性
- 符合
printf "42 20 10" | ./a.out
现在我写了这个但是我觉得它很丑而且太复杂了:
#include <stdio.h>
#include <stdbool.h>
int read_integer(char *text, int min, int max, int nom) {
int n;
bool failure = false;
do {
printf("%s [%d] ? : ", text, nom);
// Slurp spaces
scanf("%*[\t ]");
// Hack to capture default value
char buf[2];
if (scanf("%1[\n]", buf) == 1) {
return nom;
}
if (failure = (scanf("%d", &n) == 0 || n < min || n > max)) {
if (feof(stdin)) {
printf("\n");
return nom;
}
printf("Error: value should be between %d and %d!\n\n", min, max);
scanf("%*[^\n]%*1[\n]");
}
} while(failure);
scanf("%*[^\n]%*1[\n]");
return n;
}
int main(void) {
do {
printf("You said %d\n", read_integer("What's the answer", 10, 50, 42));
} while(!feof(stdin));
}
有没有更好的方法?
目前不起作用,最后一行捕获了从未输入的42
,并且不显示新行:
$ gcc main.c && ./a.out
What's the answer [42] ? : oops
Error: value should be between 10 and 50!
What's the answer [42] ? : 100
Error: value should be between 10 and 50!
What's the answer [42] ? : You said 42
What's the answer [42] ? :
编辑
根据评论,我尝试使用 fgets
编写相同的内容,但仍然不完美。或者至少非常复杂...
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
/**
* Read an integer from `stdin`.
* @param min Minimum accepted value
* @param max Maximum accepted value
* @param nom Default value
* @return captured integer
*/
int read_integer(char *text, int min, int max, int nom) {
int n = nom;
bool failure = false;
do {
printf("%s [%d] ? : ", text, nom);
// Read user input
char line[24];
do {
if (fgets(line, sizeof(line), stdin) != line || feof(stdin)) {
exit(EXIT_FAILURE);
break;
}
} while (strchr(line, '\n') == NULL);
// Default value?
{
char *cursor = line;
while ((*cursor == ' ' || *cursor == '\t') && *cursor != '[=12=]') {
cursor++;
}
if (*cursor == '\n') {
return n;
}
}
// Not a number ?
if (sscanf(line, "%d", &n) != 1) {
printf("Error: this is not valid entry!\n\n");
continue;
}
// Not in the range ?
if (n < min || n > max) {
printf("Error: value should be between %d and %d!\n\n", min, max);
continue;
}
return n;
} while(true);
}
int main() {
do {
printf("You said %d\n",
read_integer("What's the answer", 10, 50, 42));
} while(!feof(stdin));
}
使用 fgets 和 strtol,如果 strtol 忽略了额外的字符,请不要忘记抱怨(使用可选的 endptr 进行检查)。将 fgets 和 strtol 放入函数中,并在对该函数的调用周围添加验证,这样您就不会每次都重复相同的代码当你读取一个整数时。
我想实现一个读取整数的函数,但这个函数应该是:
- 适应
\n
- 对
^D
(EOF
) 的鲁棒性
- 符合
printf "42 20 10" | ./a.out
现在我写了这个但是我觉得它很丑而且太复杂了:
#include <stdio.h>
#include <stdbool.h>
int read_integer(char *text, int min, int max, int nom) {
int n;
bool failure = false;
do {
printf("%s [%d] ? : ", text, nom);
// Slurp spaces
scanf("%*[\t ]");
// Hack to capture default value
char buf[2];
if (scanf("%1[\n]", buf) == 1) {
return nom;
}
if (failure = (scanf("%d", &n) == 0 || n < min || n > max)) {
if (feof(stdin)) {
printf("\n");
return nom;
}
printf("Error: value should be between %d and %d!\n\n", min, max);
scanf("%*[^\n]%*1[\n]");
}
} while(failure);
scanf("%*[^\n]%*1[\n]");
return n;
}
int main(void) {
do {
printf("You said %d\n", read_integer("What's the answer", 10, 50, 42));
} while(!feof(stdin));
}
有没有更好的方法?
目前不起作用,最后一行捕获了从未输入的42
,并且不显示新行:
$ gcc main.c && ./a.out
What's the answer [42] ? : oops
Error: value should be between 10 and 50!
What's the answer [42] ? : 100
Error: value should be between 10 and 50!
What's the answer [42] ? : You said 42
What's the answer [42] ? :
编辑
根据评论,我尝试使用 fgets
编写相同的内容,但仍然不完美。或者至少非常复杂...
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
/**
* Read an integer from `stdin`.
* @param min Minimum accepted value
* @param max Maximum accepted value
* @param nom Default value
* @return captured integer
*/
int read_integer(char *text, int min, int max, int nom) {
int n = nom;
bool failure = false;
do {
printf("%s [%d] ? : ", text, nom);
// Read user input
char line[24];
do {
if (fgets(line, sizeof(line), stdin) != line || feof(stdin)) {
exit(EXIT_FAILURE);
break;
}
} while (strchr(line, '\n') == NULL);
// Default value?
{
char *cursor = line;
while ((*cursor == ' ' || *cursor == '\t') && *cursor != '[=12=]') {
cursor++;
}
if (*cursor == '\n') {
return n;
}
}
// Not a number ?
if (sscanf(line, "%d", &n) != 1) {
printf("Error: this is not valid entry!\n\n");
continue;
}
// Not in the range ?
if (n < min || n > max) {
printf("Error: value should be between %d and %d!\n\n", min, max);
continue;
}
return n;
} while(true);
}
int main() {
do {
printf("You said %d\n",
read_integer("What's the answer", 10, 50, 42));
} while(!feof(stdin));
}
使用 fgets 和 strtol,如果 strtol 忽略了额外的字符,请不要忘记抱怨(使用可选的 endptr 进行检查)。将 fgets 和 strtol 放入函数中,并在对该函数的调用周围添加验证,这样您就不会每次都重复相同的代码当你读取一个整数时。