我需要使用 scanf() 结果验证输入是否存在任何无效字符,包括空白字符。
我写了这个函数:
bool is_input_valid(double* inp_ptr) {
bool isInputValid = false;
char newline = 0;
int result = scanf("%lf%c", inp_ptr, &newline);
rewind(stdin);
if (result == 2 && newline == '\n') {
isInputValid = true;
}
else {
printf("Error! Invalid input format!\n");
rewind(stdin);
}
return isInputValid;
}
问题是它不适用于输入开头的空格(因此当我输入 [134] 时,它被视为有效输入)。我该如何修改我的函数来处理这种情况?
与许多
scanf
说明符一样,%lf
会消耗并丢弃所有前导空白字符,这使得检测起来很麻烦。一般来说,使用 scanf
会让你的输入流处于糟糕的状态。
(旁白:远离 scanf() 的初学者指南)
一般建议是,您最好使用
fgets
将整行输入读取到缓冲区中,并使用 strtod
解析该值,这样可以提供更多控制并提供更强大的错误处理。
strtod
does 允许可选的前导空格(按 isspace
分类),因此需要明确检查。
一个相当完整的例子:
#include <ctype.h>
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
bool is_input_valid(double *inp_ptr) {
char buffer[4096];
/* EOF / input error, or leading white space */
if (!fgets(buffer, sizeof buffer, stdin) || isspace((unsigned char) *buffer))
return false;
char *endp;
errno = 0;
double value = strtod(buffer, &endp);
if (inp_ptr)
*inp_ptr = value;
/* value did not go out of range,
* something was parsed, and
* parsing was terminated by LF or null-byte
*/
return ERANGE != errno && buffer != endp && ('\n' == *endp || !*endp);
}
int main(void)
{
while (1) {
double value;
printf(":: ");
if (is_input_valid(&value))
printf("-> %lf\n", value);
else {
if (ferror(stdin) || feof(stdin))
break;
puts("-> invalid input");
}
}
}
:: abc
-> invalid input
:: 123.456abc
-> invalid input
:: 123.456
-> invalid input
:: 123.456
-> 123.456000
:: 0x123
-> 291.000000
::
不过,我倾向于将 I/O 和验证分开。