如何验证 scanf 数字输入
How can I validate scanf numeric input
我有一个简单的问题。我正在学习链表,我的数据类型是 int
。我想知道如何在不允许接受字母输入的地方验证它。
当我按照我的方式执行时,它不打印消息。那么我怎样才能正确地做这样的事情呢?
我的代码:
node * createLinkedList()
{
node * head = NULL;
node * temp = NULL;
node * p = NULL;
while ((scanf("%d", &(temp->data))) !=EOF )
{
if ((temp->data>= 'a' && temp->data<= 'z') || (temp->data>= 'A' && temp->data<= 'Z')){
fprintf(stderr,"ERROR alph is not allowed");
}
}
}
%d
scanf
说明符是读int
,如果输入的是字母字符,scanf
会读不出来,temp->data
会保留其先前的值。
您的周期更好的条件是:
while (scanf("%d", &(temp->data)) == 0) //while input is not correctly parsed...
{
fprintf(stderr, "ERROR alph is not allowed\n"); //...print error message...
int c;
while((c = getchar()) != '\n' && c != EOF) {} //...and clear stdin
}
这样一来,如果输入无法解析为 int
,则会询问新的输入,直到输入合适的值。
请注意,, an input starting with a digit but that contains alphabetic characters will be parsed until the character is found, i.e. 123abc4
will parse 123
. If you want to avoid this, using a combination of fgets
and strtoll
是一种更可靠的方法,可以让您像上面那样验证输入。
如果您希望允许这种情况发生,并继续使用 scanf
,请不要忘记您需要清除 stdin
,其中将包含未解析的字符,(在示例案例 abc4\n
) 中,在要求新输入之前, 完美地涵盖了这些问题,检查一下。
您显示的代码还有一个问题,您的 temp
指针是 NULL
,它无法存储任何数据。要么将其声明为对象,要么为其分配内存。实际上,它导致 undefined behavior.
如果您需要验证整数输入,则不能仅依赖 %d
。它不会捕获并拒绝像 "12w45"
这样的错误条目 - 它会成功转换和分配 "12"
但会在输入流中留下 "w45"
以扰乱下一次读取。
有两种解决方法。一种是在输入后立即扫描并检查字符,例如:
int tmp;
char dummy = 0;
int r;
if ( (r = scanf( "%d%c", &tmp, &dummy )) == 2 )
{
// if following character is whitespace, then this is a valid numeric input
if ( isspace( dummy ) )
temp->data = tmp;
else
{
fprintf( stderr, "non-numeric character '%c' (%d) detected in input\n",
isprint( dummy ) ? dummy : '.', dummy );
fprintf( stderr, "clearing out input stream\n" );
while ( getchar() != '\n' )
; // empty loop
}
}
else if ( r == 1 ) // only thing following numeric input was EOF
{
temp->data = tmp;
}
else if ( r == 0 )
{
fprintf( stderr, "Non-numeric input detected, clearing input stream\n" );
while ( getchar() != '\n' )
; // empty loop
}
else
{
fprintf( stderr, "EOF or error detected on input\n" );
}
我认为更好的方法是避免完全使用 scanf
- 使用 fgets
将输入作为字符串读取,然后使用 strtol
或 strtod
执行转换:
char buffer[13]; // 11 decimal digits plus sign plus string terminator;
// should be able to store the decimal string representation
// of any 32-bit integer;
if ( fgets( buffer, sizeof buffer, stdin ) )
{
// chk will point to the first character *not* converted by strtol
char *chk;
int tmp = (int) strtol( buffer, &chk, 10 );
if ( !isspace( *chk ) && *chk != 0 )
{
fprintf( stderr, "Detected non-numeric character '%c' (%d) in input\n",
isprint( *chk ) ? *chk : '.', *chk );
}
else
{
temp->data = tmp;
}
}
else
{
fprintf( stderr, "EOF or error on input\n" );
}
当你进行这种验证时,使用一个临时文件来存储转换后的值,直到你完成;在您确定该值合适之前,请不要更新您的实际目标。
我有一个简单的问题。我正在学习链表,我的数据类型是 int
。我想知道如何在不允许接受字母输入的地方验证它。
当我按照我的方式执行时,它不打印消息。那么我怎样才能正确地做这样的事情呢?
我的代码:
node * createLinkedList()
{
node * head = NULL;
node * temp = NULL;
node * p = NULL;
while ((scanf("%d", &(temp->data))) !=EOF )
{
if ((temp->data>= 'a' && temp->data<= 'z') || (temp->data>= 'A' && temp->data<= 'Z')){
fprintf(stderr,"ERROR alph is not allowed");
}
}
}
%d
scanf
说明符是读int
,如果输入的是字母字符,scanf
会读不出来,temp->data
会保留其先前的值。
您的周期更好的条件是:
while (scanf("%d", &(temp->data)) == 0) //while input is not correctly parsed...
{
fprintf(stderr, "ERROR alph is not allowed\n"); //...print error message...
int c;
while((c = getchar()) != '\n' && c != EOF) {} //...and clear stdin
}
这样一来,如果输入无法解析为 int
,则会询问新的输入,直到输入合适的值。
请注意,123abc4
will parse 123
. If you want to avoid this, using a combination of fgets
and strtoll
是一种更可靠的方法,可以让您像上面那样验证输入。
如果您希望允许这种情况发生,并继续使用 scanf
,请不要忘记您需要清除 stdin
,其中将包含未解析的字符,(在示例案例 abc4\n
) 中,在要求新输入之前,
您显示的代码还有一个问题,您的 temp
指针是 NULL
,它无法存储任何数据。要么将其声明为对象,要么为其分配内存。实际上,它导致 undefined behavior.
如果您需要验证整数输入,则不能仅依赖 %d
。它不会捕获并拒绝像 "12w45"
这样的错误条目 - 它会成功转换和分配 "12"
但会在输入流中留下 "w45"
以扰乱下一次读取。
有两种解决方法。一种是在输入后立即扫描并检查字符,例如:
int tmp;
char dummy = 0;
int r;
if ( (r = scanf( "%d%c", &tmp, &dummy )) == 2 )
{
// if following character is whitespace, then this is a valid numeric input
if ( isspace( dummy ) )
temp->data = tmp;
else
{
fprintf( stderr, "non-numeric character '%c' (%d) detected in input\n",
isprint( dummy ) ? dummy : '.', dummy );
fprintf( stderr, "clearing out input stream\n" );
while ( getchar() != '\n' )
; // empty loop
}
}
else if ( r == 1 ) // only thing following numeric input was EOF
{
temp->data = tmp;
}
else if ( r == 0 )
{
fprintf( stderr, "Non-numeric input detected, clearing input stream\n" );
while ( getchar() != '\n' )
; // empty loop
}
else
{
fprintf( stderr, "EOF or error detected on input\n" );
}
我认为更好的方法是避免完全使用 scanf
- 使用 fgets
将输入作为字符串读取,然后使用 strtol
或 strtod
执行转换:
char buffer[13]; // 11 decimal digits plus sign plus string terminator;
// should be able to store the decimal string representation
// of any 32-bit integer;
if ( fgets( buffer, sizeof buffer, stdin ) )
{
// chk will point to the first character *not* converted by strtol
char *chk;
int tmp = (int) strtol( buffer, &chk, 10 );
if ( !isspace( *chk ) && *chk != 0 )
{
fprintf( stderr, "Detected non-numeric character '%c' (%d) in input\n",
isprint( *chk ) ? *chk : '.', *chk );
}
else
{
temp->data = tmp;
}
}
else
{
fprintf( stderr, "EOF or error on input\n" );
}
当你进行这种验证时,使用一个临时文件来存储转换后的值,直到你完成;在您确定该值合适之前,请不要更新您的实际目标。