我可以用什么来代替scanf进行输入转换?
我经常看到人们劝阻其他人使用scanf,并说有更好的选择。然而,我最后看到的不是“不要使用我可以用什么来代替scanf进行输入转换?,c,scanf,C,Scanf,我经常看到人们劝阻其他人使用scanf,并说有更好的选择。然而,我最后看到的不是“不要使用scanf”就是“这里有一个正确的格式字符串”,而且从来没有提到过任何“更好的替代方案”的例子 例如,让我们以这段代码为例: scanf("%c", &c); 这将读取上次转换后输入流中留下的空白。通常建议的解决方案是使用: scanf(" %c", &c); 或者不使用scanf 既然scanf不好,那么在转换scanf通常不使用scanf就能处理的输入格式(如整数、浮点数和字符串)时
scanf
”就是“这里有一个正确的格式字符串”,而且从来没有提到过任何“更好的替代方案”的例子
例如,让我们以这段代码为例:
scanf("%c", &c);
这将读取上次转换后输入流中留下的空白。通常建议的解决方案是使用:
scanf(" %c", &c);
或者不使用scanf
既然
scanf
不好,那么在转换scanf
通常不使用scanf
就能处理的输入格式(如整数、浮点数和字符串)时,有哪些ANSI C选项?读取输入的最常见方法是:
- 通常建议使用固定大小的
,以及fgets
- 使用
,如果您只读取单个fgetc
,这可能很有用char
,将字符串转换为整数strtoll
/strof
/d
,将字符串转换为浮点数ld
,它没有简单地使用sscanf
那么糟糕,尽管它确实有下面提到的大多数缺点scanf
- 在纯ANSI C中没有解析分隔符分隔输入的好方法。可以使用POSIX中的
,也可以使用strtok_r
,这不是线程安全的。您还可以使用strtok
和strcspn
实现线程安全变体,因为strspn
不涉及任何特殊的操作系统支持strtok\u r
- 这可能有些过分,但您可以使用lexer和parser(
和flex
是最常见的示例)bison
- 无需转换,只需使用字符串即可
由于我没有在问题中详细说明为什么
scanf
不好,我将详细说明:
- 使用转换说明符
和%[…]
,%c
不会占用空白。这一点显然并不广为人知,这一点可以从许多复制品中得到证明scanf
- 在引用
的参数(特别是字符串)时,对于何时使用一元scanf
运算符存在一些混淆&
- 很容易忽略
的返回值。这很容易导致读取未初始化的变量时出现未定义的行为scanf
- 在
中很容易忘记防止缓冲区溢出scanf
与scanf(“%s”,str)
的性能一样差,甚至比
的性能差
- 使用
转换整数时无法检测溢出事实上,溢出会导致这些函数中出现溢出。scanf
当你知道你的输入总是结构良好、表现良好时,scanf就棒极了。否则 国际海事组织,以下是scanf的最大问题:
- 缓冲区溢出的风险-如果不为
和%s
转换说明符指定字段宽度,则存在缓冲区溢出的风险(尝试读取的输入超过缓冲区的大小)。不幸的是,没有好的方法将其指定为参数(与%[
一样)-您必须将其硬编码为转换说明符的一部分,或者进行一些宏操作printf
- 接受应该拒绝的输入-如果您正在使用
转换说明符读取输入,并且键入类似%d
的内容,您可能希望12w4
拒绝该输入,但它没有-它成功地转换并分配scanf
,将12
保留在输入流将阻塞下一次读取w4
fgets
将所有交互式输入作为文本读取-它允许您指定一次读取的最大字符数,因此可以轻松防止缓冲区溢出:
char input[100];
if ( !fgets( input, sizeof input, stdin ) )
{
// error reading from input stream, handle as appropriate
}
else
{
// process input buffer
}
fgets
的一个怪癖是,如果有空间,它将在缓冲区中存储尾随的换行符,因此您可以轻松检查是否有人输入了超出预期的输入:
char *newline = strchr( input, '\n' );
if ( !newline )
{
// input longer than we expected
}
如何处理这一点取决于您自己-您可以立即拒绝整个输入,并使用getchar
清除任何剩余的输入:
while ( getchar() != '\n' )
; // empty loop
或者,你也可以处理到目前为止得到的输入,然后重新阅读。这取决于你试图解决的问题
要对输入进行标记化(根据一个或多个分隔符将其拆分),可以使用strtok
,但要注意-strtok
修改其输入(它用字符串终止符覆盖分隔符),并且不能保留其状态(也就是说,您不能部分标记一个字符串,然后开始标记另一个字符串,然后从原始字符串中的中断处开始)。有一个变体,strtok_s
,它保留标记器的状态,但它的实现是可选的(您需要检查\uuuuu STDC\u LIB\u EXT1\uuuu
是否已定义,以查看它是否可用)
将输入标记化后,如果需要将字符串转换为数字(即,“1234”
=>1234
),您可以选择。strtol
和strtod
将整数和实数的字符串表示形式转换为各自的类型。它们还允许您捕获上面提到的12w4
问题-它们的参数之一是指向字符串中未转换的第一个字符的指针:
char *text = "12w4";
char *chk;
long val;
long tmp = strtol( text, &chk, 10 );
if ( !isspace( *chk ) && *chk != 0 )
// input is not a valid integer string, reject the entire input
else
val = tmp;
太长,读不下去了
fgets
用于获取输入。sscanf
用于
#define bsize 100
void error_function(const char *buffer, int no_conversions) {
fprintf(stderr, "An error occurred. You entered:\n%s\n", buffer);
fprintf(stderr, "%d successful conversions", no_conversions);
exit(EXIT_FAILURE);
}
char c, buffer[bsize];
int x,y;
float f, g;
int r;
printf("Enter two integers: ");
fflush(stdout); // Make sure that the printf is executed before reading
if(! fgets(buffer, bsize, stdin)) error_function(buffer, 0);
if((r = sscanf(buffer, "%d%d", &x, &y)) != 2) error_function(buffer, r);
// Unless the input buffer was to small we can be sure that stdin is empty
// when we come here.
printf("Enter two floats: ");
fflush(stdout);
if(! fgets(buffer, bsize, stdin)) error_function(buffer, 0);
if((r = sscanf(buffer, "%f%f", &f, &g)) != 2) error_function(buffer, r);
// Reading single characters can be especially tricky if the input buffer
// is not emptied before. But since we're using fgets, we're safe.
printf("Enter a char: ");
fflush(stdout);
if(! fgets(buffer, bsize, stdin)) error_function(buffer, 0);
if((r = sscanf(buffer, "%c", &c)) != 1) error_function(buffer, r);
printf("You entered %d %d %f %c\n", x, y, f, c);
int printfflush (const char *format, ...)
{
va_list arg;
int done;
va_start (arg, format);
done = vfprintf (stdout, format, arg);
fflush(stdout);
va_end (arg);
return done;
}
char *convertStringToInteger(int *outValue, char *string, int minValue, int maxValue) {
return "Code not implemented yet!";
}
int main(int argc, char *argv[]) {
char *errorString;
int value;
if(argc < 2) {
printf("ERROR: No command line argument.\n");
return EXIT_FAILURE;
}
errorString = convertStringToInteger(&value, argv[1], -10, 2000);
if(errorString != NULL) {
printf("ERROR: %s\n", errorString);
return EXIT_FAILURE;
}
printf("SUCCESS: Your number is %d\n", value);
return EXIT_SUCCESS;
}
char line[512];
printf("type something:\n");
fgets(line, 512, stdin);
printf("you typed: %s", line);
printf("type an integer:\n");
fgets(line, 512, stdin);
int i = atoi(line);
printf("type a floating-point number:\n");
fgets(line, 512, stdin);
float f = atof(line);
printf("you typed %d and %f\n", i, f);
printf("type a character:\n");
fgets(line, 512, stdin);
char c = line[0];
printf("you typed %c\n", c);
hello world!
fgets(line, 512, stdin);
#define MAXLINE 512
char line[MAXLINE];
fgets(line, MAXLINE, stdin);
fgets(line, sizeof(line), stdin);
printf("type something:\n");
if(fgets(line, 512, stdin) == NULL) {
printf("Well, never mind, then.\n");
exit(1);
}
printf("you typed: \"%s\"\n", line);
you typed: "Steve
"
char *p = strchr(line, '\n');
if(p != NULL) *p = '\0';
strtok(line, "\n");
line[strcspn(line, "\n")] = '\0';
// scanf("%d %f fred", &some_int, &some_float);
#define EXPECTED_LINE_MAX 100
char buffer[EXPECTED_LINE_MAX * 2]; // Suggest 2x, no real need to be stingy.
if (fgets(buffer, sizeof buffer, stdin)) {
int n = 0;
// add -------------> " %n"
sscanf(buffer, "%d %f fred %n", &some_int, &some_float, &n);
// Did scan complete, and to the end?
if (n > 0 && buffer[n] == '\0') {
// success, use `some_int, some_float`
} else {
; // Report bad input and handle desired.
}
/* This scanner reads a file of numbers, expecting one number per line. It */
/* allows for the use of European-style comma as decimal point. */
%{
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#ifdef WINDOWS
#include <io.h>
#endif
#include "Point.h"
#define YY_NO_UNPUT
#define YY_DECL int f_lex (double *val)
double atofEuro (char *);
%}
%option prefix="f_"
%option nounput
%option noinput
EURONUM [-+]?[0-9]*[,]?[0-9]+([eE][+-]?[0-9]+)?
NUMBER [-+]?[0-9]*[\.]?[0-9]+([eE][+-]?[0-9]+)?
WS [ \t\x0d]
%%
[!@#%&*/].*\n
^{WS}*{EURONUM}{WS}* { *val = atofEuro (yytext); return (1); }
^{WS}*{NUMBER}{WS}* { *val = atof (yytext); return (1); }
[\n]
.
%%
/*------------------------------------------------------------------------*/
int scan_f (FILE *in, double *vals, int max)
{
double *val;
int npts, rc;
f_in = in;
val = vals;
npts = 0;
while (npts < max)
{
rc = f_lex (val);
if (rc == 0)
break;
npts++;
val++;
}
return (npts);
}
/*------------------------------------------------------------------------*/
int f_wrap ()
{
return (1);
}