正如几乎所有人所说,最好使用它fgets(..., stdin)
来处理这个问题。
在下面的链接中,我提出了一种安全且正确的技术,让您可以scanf()
通过可靠的宏替换为更安全的方法:
安全替换scanf()的宏
我提出的宏(使用兼容的C99编译器)是safe_scanf()
,如以下程序所示:
#include <stdio.h>
#define safe_scanf(fmt, maxb, ...) { \
char buffer[maxb+1] = { [maxb - 1] = '\0' }; \
fgets(buffer, maxb+1, stdin); \
if ((buffer[maxb - 1] != '\0') && (buffer[maxb - 1] != '\n')) \
while(getchar() != '\n') \
; \
sscanf(buffer, fmt, __VA_ARGS__); \
}
#define MAXBUFF 20
int main(void) {
int x; float f;
safe_scanf("%d %g", MAXBUFF+1, &x, &f);
printf("Your input was: x == %d\t\t f == %g", x, f);
return 0;
}
您必须根据需要调整的值MAXBUFF
......
虽然宏safe_scanf()
非常可靠,
但使用宏方法有一些弱点:
缺少参数类型检查,缺少“返回”值(这与"true"scanf()
函数,它返回一个int,带有用于错误检查的有价值的信息),等等。
所有这些问题都有解决方案,但它是另一个主题的一部分......
也许,最准确的解决方案是定义一个my_scanf()
具有可变数量参数的函数,通过调用stdarg.h
库,连接到fgets()
和的组合vsscanf()
。这里有代码:
#include <stdio.h>
#include <stdarg.h>
int my_scanf(const char* fmt, const unsigned int maxbuff, ...) {
va_list ptr;
int ret;
if (maxbuff <= 0)
return EOF; /* Bad size for buffer[] */
char buffer[maxbuff+1];
buffer[maxbuff-1] = '\0'; /* Quick buffer cleaning... */
if (fgets(buffer, maxbuff+1, stdin) == NULL)
return EOF; /* Error detected */
else {
if ((buffer[maxbuff-1] != '\n') && (buffer[maxbuff-1] != '\0'))
/* Condition logically equivalent to:
fgets() has not reached an '\n'
*/
while (getchar() != '\n')
; /* "Flushing" stdin... */
va_start(ptr, maxbuff);
ret = vsscanf(buffer, fmt, ptr);
va_end(ptr);
return ret;
}
}
#define MAXBUFF 20
int main(void) {
int x;
float z;
int scanf_ret = my_scanf("%d %g", MAXBUFF, &x, &z);
printf("\nTest:\n x == %d\n z == %g\n scanfret == %d", x, z, scanf_ret);
getchar();
return 0;
}
函数 my_scanf() 具有原型
int my_scanf(const char* fmt, const int maxbuff, ...);
它接受一个格式字符串fmt
,其行为方式与任何其他scanf()
-like 相同。
第二个参数是从标准输入(键盘)有效接受的最大字符数。
返回值是一个int,EOF
如果maxbuff
没有意义,或者发生了一些输入错误。如果返回非负值,则与标准函数sscanf()
或vsscanf()
.
在函数内部,maxbuff
以 1 递增,因为fgets()
为额外的 '\0' 字符腾出了一些空间。
的非正值maxbuff
立即被丢弃。
fgets()
将读取从stdin
(键盘)读取的字符串,最多可容纳maxbuff
字符,包括'\n'。
如果用户输入了一个很长的字符串,那么它将被截断,并且需要某种“刷新”机制才能将所有字符丢弃到下一个 '\n' ( ENTER )。如果不是,则下一个键盘读数可能包含较旧的字符,根本不需要。
"flushing" 的条件是 readfgets()
后还没有达到 '\n' stdin
。
当且仅当,buffer[maxbuff - 1]
不等于“\0”也不等于“\n”。
(检查一下!)最后,使用宏和函数
的适当组合来处理参数的变量列表。 stdarg.h
vsscanf()