我希望代码一直运行,直到用户输入整数值。
该代码适用于
char
和 char
数组。
我已完成以下操作:
#include <stdio.h>
int main()
{
int n;
printf("Please enter an integer: ");
while(scanf("%d",&n) != 1)
{
printf("Please enter an integer: ");
while(getchar() != '\n');
}
printf("You entered: %d\n",n);
return 0;
}
问题是如果用户输入浮点值
scanf
会接受它。
Please enter an integer: abcd
Please enter an integer: a
Please enter an integer: 5.9
You entered: 5
如何避免这种情况?
scanf()
。fgets()
来获取整行。strtol()
将行解析为整数,检查它是否消耗了整行。char *end;
char buf[LINE_MAX];
do {
if (!fgets(buf, sizeof buf, stdin))
break;
// remove \n
buf[strlen(buf) - 1] = 0;
int n = strtol(buf, &end, 10);
} while (end != buf + strlen(buf));
使用
fgets
和 strtol
,
指向
s
中整数表示后的第一个字符的指针存储在 p
指向的对象中,如果 *p
与 \n
不同,则输入错误。
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
char *p, s[100];
long n;
while (fgets(s, sizeof(s), stdin)) {
n = strtol(s, &p, 10);
if (p == s || *p != '\n') {
printf("Please enter an integer: ");
} else break;
}
printf("You entered: %ld\n", n);
return 0;
}
如果您决定使用
scanf
,您可以执行以下操作:
int val;
char follow;
int read = scanf( "%d%c", &val, &follow );
if ( read == 2 )
{
if ( isspace( follow ) )
{
// input is an integer followed by whitespace, accept
}
else
{
// input is an integer followed by non-whitespace, reject
}
}
else if ( read == 1 )
{
// input is an integer followed by EOF, accept
}
else
{
// input is not an integer, reject
}
尝试在
scanf
中使用以下模式。它将一直读到行尾:
scanf("%d\n", &n)
循环内不需要
getchar()
,因为 scanf
将读取整行。浮点数与 scanf
模式不匹配,提示将再次要求输入整数。
我知道如何使用
和fgets
来完成此操作,我想知道如何使用strtol
来完成此操作(如果可能)。scanf()
正如其他答案所说,
scanf
并不真正适合于此,fgets
和strtol
是一种替代方案(尽管fgets
的缺点是很难检测到输入中的0字节并且不可能告诉 0 字节后输入了什么(如果有)。
为了完整起见(并假设有效输入是一个整数后跟一个换行符):
while(scanf("%d%1[\n]", &n, (char [2]){ 0 }) < 2)
或者,在
%n
之前和之后使用 %*1[\n]
并进行赋值抑制。但请注意(来自 Debian 手册页):
这不是转换,尽管可以使用
赋值抑制字符来抑制它。 C 标准规定:“执行*
指令不会增加执行完成时返回的赋值计数”,但勘误表似乎与此相矛盾。 也许明智的做法是不要对%n
转换对返回值的影响做出任何假设。%n
一个可能的解决方案是向后思考:接受浮点数作为输入,如果浮点数不是整数则拒绝输入:
int n;
float f;
printf("Please enter an integer: ");
while(scanf("%f",&f)!=1 || (int)f != f)
{
...
}
n = f;
虽然这确实允许用户输入 12.0 或 12e0 等内容。
使用
fgets()
效果更好。
要解决仅使用
scanf()
输入的问题,请扫描 int
和以下 char
。
int ReadUntilEOL(void) {
char ch;
int count;
while ((count = scanf("%c", &ch)) == 1 && ch != '\n')
; // Consume char until \n or EOF or IO error
return count;
}
#include<stdio.h>
int main(void) {
int n;
for (;;) {
printf("Please enter an integer: ");
char NextChar = '\n';
int count = scanf("%d%c", &n, &NextChar);
if (count >= 1 && NextChar == '\n')
break;
if (ReadUntilEOL() == EOF)
return 1; // No valid input ever found
}
printf("You entered: %d\n", n);
return 0;
}
如果用户仅输入空格(例如仅输入Enter),此方法不会重新提示。
虽然通常不建议使用
scanf
进行基于行的用户输入,但我将首先提出一个使用 scanf
的解决方案。稍后我会提出一个更好的不使用scanf
的解决方案。
scanf
解决方案的工作原理是检查行上未被 scanf
消耗的剩余字符。一般来说,这应该只是换行符。但是,由于使用 %d
说明符和 scanf
接受前导 空白字符 ,因此如果程序也接受尾随空白字符,那么它将是一致的。
这是我的解决方案,使用
scanf
:
#include <stdio.h>
#include <ctype.h>
int main( void )
{
int n;
//repeat until input is valid
for (;;) //infinite loop, equivalent to while(1)
{
int c;
//prompt user for input
printf( "Please enter an integer: " );
//attempt to read and convert input
if ( scanf( "%d", &n ) == 1 )
{
//verify that remainder of input only consists of
//whitespace characters
while ( ( c = getchar() ) != EOF && c != '\n' )
{
if ( !isspace(c) )
{
//we cannot use "break" here, because we want
//to break out of the outer loop, not the inner
//loop
goto invalid_input;
}
}
//input is valid
break;
}
invalid_input:
//print error message
printf( "Input is invalid!\n" );
//discard remainder of line
do
{
c = getchar();
} while ( c != EOF && c != '\n' );
}
printf("You entered: %d\n",n);
return 0;
}
该程序具有以下行为:
Please enter an integer: abc
Input is invalid!
Please enter an integer: 6abc
Input is invalid!
Please enter an integer: 6.7
Input is invalid!
Please enter an integer: 6
You entered: 6
此
scanf
解决方案存在以下问题:
int
的数字(例如大于 INT_MAX
的数字),程序将出现未定义的行为。
%d
的 scanf
说明符消耗了所有前导空白字符。fgets
和strtol
来解决,而不是函数scanf
。
执行所有这些验证检查会使代码变得相当大。因此,将所有代码放入其自己的函数中是有意义的。这是一个使用函数
get_int_from_user
的示例,我从这个 回答另一个问题:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <errno.h>
int get_int_from_user( const char *prompt );
int main( void )
{
int number;
number = get_int_from_user( "Please enter an integer: " );
printf( "Input was valid.\n" );
printf( "The number is: %d\n", number );
return 0;
}
int get_int_from_user( const char *prompt )
{
//loop forever until user enters a valid number
for (;;)
{
char buffer[1024], *p;
long l;
//prompt user for input
fputs( prompt, stdout );
//get one line of input from input stream
if ( fgets( buffer, sizeof buffer, stdin ) == NULL )
{
fprintf( stderr, "Unrecoverable input error!\n" );
exit( EXIT_FAILURE );
}
//make sure that entire line was read in (i.e. that
//the buffer was not too small)
if ( strchr( buffer, '\n' ) == NULL && !feof( stdin ) )
{
int c;
printf( "Line input was too long!\n" );
//discard remainder of line
do
{
c = getchar();
if ( c == EOF )
{
fprintf( stderr, "Unrecoverable error reading from input!\n" );
exit( EXIT_FAILURE );
}
} while ( c != '\n' );
continue;
}
//attempt to convert string to number
errno = 0;
l = strtol( buffer, &p, 10 );
if ( p == buffer )
{
printf( "Error converting string to number!\n" );
continue;
}
//make sure that number is representable as an "int"
if ( errno == ERANGE || l < INT_MIN || l > INT_MAX )
{
printf( "Number out of range error!\n" );
continue;
}
//make sure that remainder of line contains only whitespace,
//so that input such as "6sdfj23jlj" gets rejected
for ( ; *p != '\0'; p++ )
{
if ( !isspace( (unsigned char)*p ) )
{
printf( "Unexpected input encountered!\n" );
//cannot use `continue` here, because that would go to
//the next iteration of the innermost loop, but we
//want to go to the next iteration of the outer loop
goto continue_outer_loop;
}
}
return l;
continue_outer_loop:
continue;
}
}
该程序具有以下行为:
Please enter an integer: abc
Error converting string to number!
Please enter an integer: 6abc
Unexpected input encountered!
Please enter an integer: 6.7
Unexpected input encountered!
Please enter an integer: 6000000000
Number out of range error!
Please enter an integer: 6
Input was valid.
The number is: 6
也许不是最佳解决方案..但仅使用 scanf 和 strtol
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef enum {false, true} bool;
bool isIntString(char* input ) {
int i;
for (i = 0; i < strlen(input); i++) {
if(input[i] < '0' || input[i] > '9') {
return false;
}
}
return true;
}
int main()
{
char s[10];
char *end;
bool correct = false;
int result;
printf("Type an integer value: ");
do {
scanf("%s",s);
if ( isIntString(s)) {
correct = true;
result = strtol(s, &end, 10);
break;
} else {
printf("you typed %s\n",s);
printf("\ntry again: ");
}
} while (!correct);
printf("Well done!! %d\n",result);
return 0;
}