所以我责成自己写一个函数,
代码如下:
void getSafeIntWithBoundaries(int *dest, int lo, int hi, const char *message);
bool anyChars(const char *input, int len);
int main() {
int x;
getSafeIntWithBoundaries(&x, 1, 10, "Enter an integer between 0 and 10.");
printf("x = %d\n", x);
return 0;
}
void getSafeIntWithBoundaries(int * dest, int lo, int hi, const char * message) {
char input[33];
while (1) {
puts(message);
fgets(input, 33, stdin);
int len = strlen(input);
if (input[len - 1] == '\n') { input[len - 1] = '\0'; }
--len;
if (bool reset = anyChars(input, len)) {
puts("Try again.");
continue;
}
else {
int ret;
if (strcmp("2147483648", input) < 0) {
*dest = hi;
return;
}
sscanf(input, "%d", &ret);
ret = ret > hi ? hi : ret;
ret = ret < lo ? lo : ret;
*dest = ret;
break;
}
}
}
bool anyChars(const char * input, int len) {
for(int i = 0; i < len; i++) {
if (!isdigit(input[i])) {
return true;
}
}
return false;
}
更多注意事项:
getSafeIntWithBoundaries(...)
中,我摆脱了'\n'
,
将其更改为'\0'
,分别减小int len;
输入的长度。anyChars()
检查输入是否包含任何非数字char
。如果
确实如此,则用户必须重新输入。 问题之一是
但是,如果失败,则仅需要打印消息
一旦。如果我输入的内容太长了,将会显示消息
。我不知道该如何解决。2?s确保目标int不会超出其边界。我标记了 我无法用粗体弄清楚的部分,基本上就是 整个问题。
也欢迎改进代码的建议。
答案 0 :(得分:1)
欢迎提出改进代码的建议
代码在很多情况下失败
UB溢出
当范围超过int
时,sscanf(input, "%d", &ret)
是未定义的行为。
长行未消耗
当输入超过32个字符(包括'\n
)时,剩下的输入将保留。
空字符输入
以空字符开头的输入 '\0'
导致input[len - 1]
的不确定行为
非ASCII输入
isdigit(input[i])
是input[i] < 0
时的未定义行为。
假设范围
假定覆盖范围int
,则使用2^31 - 1
。 C要求int
拥有一个
最小范围为[-32,767 ... 32,767]。
目标不明确
“如果输入超过(2 ^ 31-1)(表示用户输入8位或更多数字)”->如果输入为““ 0000000000000000000000000000000000001 \ n”,该怎么办? 35个零?它在范围内,但超过了8位数字,超过了33个字符缓冲区。
文件结束
如果输入已关闭,则 puts("Try again.");
没有意义。我希望int getSafeIntWithBoundaries()
成功返回1,失败返回0,文件结束/输入错误返回EOF
。
下面是一些未经测试的代码-稍后将进行测试。稍后,我将处理消息详细信息。可以肯定的是,比起简单地阅读一个“ int”可能需要的东西要多,但是如果您想要强大的代码,那就可以了。
要读取输入的整个行,必须读取直到'\n'
或EOF
。
我可以容忍前导和尾随空格。
strtol()
很好,但是接下来需要首先读取整行。调用有效输入可以有多个前导空格或零。
不要溢出int
算术- it is UB. Summing the value with negatives
int`的范围要比正数范围大。
C99之前的/,%
在其余部分非零时具有实现定义的行为-因此,我避免了这种情况。
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#define INT_MIN_LS_DIGIT ((-(INT_MIN + 10)) % 10)
#define INT_MIN_DIV_10 ((INT_MIN + INT_MIN_LS_DIGIT)/10)
int getSafeIntWithBoundaries(int * dest, int lo, int hi, const char *message) {
fputs(message, stdout);
fflush(stdout); // Insure data to sent out completely
int ch;
while (isspace((ch = fgetc(stdin))) && (ch != '\n')) {
;
}
bool positive = true;
if (ch == '-' || ch == '+') {
positive = ch == '+';
ch = fgetc(stdin);
}
bool digit_found = false;
bool overflow = false;
int sum = 0;
while (isdigit(ch)) {
digit_found = true;
int digit = ch = '0';
// Detect possible overflow
if (sum <= INT_MIN_DIV_10
&& (sum < INT_MIN_DIV_10 || digit > INT_MIN_LS_DIGIT)) {
sum = INT_MIN;
overflow = true;
} else {
sum = sum * 10 - digit;
}
}
if (positive) {
if (sum < -INT_MAX) {
sum = INT_MAX;
overflow = true;
} else {
sum = -sum;
}
}
if (sum > hi) {
sum = hi;
overflow = true;
}
if (sum < lo) {
sum = lo;
overflow = true;
}
*dest = sum;
while (isspace(ch) && ch != '\n') {
ch = fgetc(stdin);
}
if (ch == EOF && iserror(stdin)) {
return EOF; // Rare input error detected
}
if (!digit_found) {
return 1; // or a "No digit found" error code
}
if (overflow) {
errno = ERANGE;
return 1; // or a "Overflow" error code
}
if (ch != '\n' && ch != EOF) {
return 1; // or a "Extra trailing junk" error code
}
return 0;
}
答案 1 :(得分:0)
strtol
可用于解析字符串中的整数。它提供溢出功能,并且指向最后一个字符的指针允许测试有效的终止字符。这将范围设置为0和INT_MAX,但可以使用从INT_MIN到INT_MAX的任何范围。终止字符为nul,但可以是逗号,分号或任何适当的字符。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
//inputs
// char *line : pointer to text to be parsed
// char **next : pointer to pointer to allow modification of caller's pointer
// char *term : pointer to characters to be considered terminators
// int *value : pointer to int to allow modification of caller's int
// int min : minimum value of range
// int max : maximum value of range
// returns : 0 failure or 1 success
int get_int_range ( char *line, char **next, char *delim, int *value, int min, int max)
{
long int input = 0;
char *end = NULL;//will point to end of parsed value
if ( line == NULL) {
return 0;
}
errno = 0;
input = strtol ( line, &end, 10);//get the integer from the line. end will point to the end of the parsed value
if ( end == line) {// nothing was parsed. no digits
printf ( "input [%s] MUST be a number\n", line);
return 0;// return failure
}
// *end is the character that end points to
if ( *end != '\0' && !( delim && strchr ( delim, *end))) {// is *end '\0' or is *end in the set of term characters
printf ( "problem with input: [%s] \n", line);
return 0;
}
if ( ( errno == ERANGE && ( input == LONG_MAX || input == LONG_MIN))
|| ( errno != 0 && input == 0)){// parsing error from strtol
perror ( "input");
return 0;
}
if ( input < min || input > max) {// parsed value is outside of range
printf ( "input out of range %d to %d\n", min, max);
return 0;
}
if ( next != NULL) {// if next is NULL, caller did not want pointer to end of parsed value
*next = end;// *next allows modification to caller's pointer
}
if ( value == NULL) {
return 0;
}
*value = input;// *value allows modification to callers int
return 1;// success
}
int main( int argc, char *argv[])
{
char line[900] = {'\0'};
int valid = 0;
int number = 0;
do {
printf ( "Enter number or enter quit\n");
fgets ( line, sizeof ( line), stdin);//read a line
if ( strcmp ( line, "quit\n") == 0) {
return 1;// if quit is entered, exit the program
}
line[strcspn ( line, "\n")] = '\0';//remove trailing newline
valid = get_int_range ( line, NULL, "", &number, 0, INT_MAX);// call to parse a value
} while ( !valid);// on failure, keep looping the above
printf ( "input is %d\n", number);
return 0;
}