如何读取整行输入
线
scanf("%14s", buf);
永远不会读取整行输入。它只会读取一个输入单词(也可以由数字组成)。例如,如果用户输入了无效的输入,例如
"39 jdsuoew"
在一行上,那么它只会读取单词"39" 作为输入,而将行的其余部分留在输入流上。这意味着您的程序将接受输入作为有效输入,尽管在这种情况下它可能应该被拒绝。
即使用户只输入"39",那么它也只会读取这个数字,但会在输入流上留下换行符,即can cause trouble。
如果要确保读取整行,我建议您使用函数fgets,因为该函数将始终读取整行输入(包括换行符),假设大小为提供的内存缓冲区足够大,可以存储整行。
char line[100];
//attempt to read one line of input
if ( fgets( line, sizeof line, stdin ) == NULL )
{
fprintf( stderr, "Input error!\n" );
exit( EXIT_FAILURE );
}
//search for newline character, to verify that entire line was read in
if ( strchr( line, '\n' ) == NULL )
{
fprintf( stderr, "Line was too long for input buffer!\n" );
exit( EXIT_FAILURE );
}
请注意,函数 strchr 需要您 #include <string.h>。如果,正如您在 cmets 部分中所述,您不允许使用该头文件,那么您可能不得不假设内存缓冲区对于整行来说足够大,而无需验证它(您也在您的代码)。虽然不使用函数strchr 也可以验证这一点,但我不建议这样做。如果缓冲区足够大,那么该行不太可能(但仍有可能)不适合缓冲区。
使用strtol将字符串转换为整数
在将输入行读入内存缓冲区后,您可以使用函数sscanf 或strtol 尝试将整数转换为数字。我建议您使用函数strtol,因为如果用户输入的数字太大而无法表示为long int,则函数sscanf 具有undefined behavior,而函数strtol 能够报告这样的错误条件是可靠的。
为了将您读取的行转换为整数,您可以像这样简单地调用strtol:
long l;
l = strtol( line, NULL, 10 );
但是,调用第二个参数设置为NULL 的函数与调用函数atoi 存在相同的问题:您无法知道输入是否成功转换,或者是否发生了转换错误。而且您也无法知道有多少输入被成功转换,以及转换是否过早失败,例如由于用户输入了浮点数的小数点。
因此,最好这样调用函数:
long l;
char *p;
l = strtol( line, &p, 10 );
现在,指针p 将指向第一个未成功转换为数字的字符。在理想情况下,它将指向行尾的换行符(或者如果您不使用fgets,则可能是终止空字符)。因此,您可以验证整行是否已转换,并且至少有一个字符已转换,如下所示:
if ( p == line || *p != '\n' )
{
printf( "Error converting number!\n" );
exit( EXIT_FAILURE );
}
但是,这可能有点太严格了。例如,如果用户输入"39 "(数字后有一个空格),输入将被拒绝。在这种情况下,您可能希望接受输入。因此,与其要求 p 指向换行符并因此不接受行中的任何其他剩余字符,不如允许 whitespace characters 保留在行中,如下所示:
if ( p == line )
{
printf( "Error converting number!\n" );
exit( EXIT_FAILURE );
}
while ( *p != '\n' )
{
//verify that remaining character is whitespace character
if ( !isspace( (unsigned char)*p ) )
{
printf( "Error converting number!\n" );
exit( EXIT_FAILURE );
}
p++;
}
请注意,您必须#include <ctype.h> 才能使用函数isspace。
此外,如前所述,使用函数strtol 优于sscanf 的优势在于它可以可靠地报告数字是否太大或太小而无法表示为long int。如果发生这样的错误情况,它会将errno 设置为ERANGE。请注意,您必须#include <errno.h> 才能使用errno。
long l;
char *p;
errno = 0; //make sure that errno is not already set to ERANGE
l = strtol( line, &p, 10 );
if ( errno == ERANGE )
{
printf( "Number out of range!\n" );
exit( EXIT_FAILURE );
}
fgets和strtol的代码示例
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
int main( void )
{
char line[100], *p;
long l;
//prompt user for input
printf( "Please enter an integer: " );
//attempt to read one line of input
if ( fgets( line, sizeof line, stdin ) == NULL )
{
fprintf( stderr, "Input error!\n" );
exit( EXIT_FAILURE );
}
//search for newline character, to verify that entire line was read in
if ( strchr( line, '\n' ) == NULL )
{
fprintf( stderr, "Line was too long for input buffer!\n" );
exit( EXIT_FAILURE );
}
//make sure that errno is not already set to ERANGE
errno = 0;
//attempt to convert input to integer
l = strtol( line, &p, 10 );
//verify that conversion was successful
if ( p == line )
{
printf( "Error converting number!\n" );
exit( EXIT_FAILURE );
}
//check for range error
if ( errno == ERANGE )
{
printf( "Number out of range!\n" );
exit( EXIT_FAILURE );
}
//verify that there are either no remaining characters, or that
//all remaining characters are whitespace characters
while ( *p != '\n' )
{
//verify that remaining character is whitespace character
if ( !isspace( (unsigned char)*p ) )
{
printf( "Error converting number!\n" );
exit( EXIT_FAILURE );
}
p++;
}
//print valid input
printf( "Input is valid.\nYou entered: %ld\n", l );
}
这个程序有以下输出:
有效输入:
Please enter an integer: 39
Input is valid.
You entered: 39
在同一行有效输入后的垃圾:
Please enter an integer: 39 jdsuoew
Error converting number!
尝试输入浮点数而不是整数:
Please enter an integer: 1.0
Error converting number!
尝试输入大到无法表示为long int的数字:
Please enter an integer: 10000000000000000000000000
Number out of range!