简体   繁体   English

scanf c 输入错误

[英]scanf c wrong input

Good afternoon, my question is conceptual.下午好,我的问题是概念性的。 How can I make it generate a "fancy" error when the user incorrectly enters some data that does not correspond to the scanf() function?当用户错误地输入了一些与 scanf() function 不对应的数据时,如何使其生成“奇特”错误? So as to only allow integers to be entered in the example below (not characters or array of characters or an inappropriate data).为了只允许在下面的示例中输入整数(不是字符或字符数组或不适当的数据)。

For example:例如:

#include <stdio.h>

int a;
printf("Enter a number\n");
scanf("%d", &a); //the user is supposed to enter a number
printf("Your number is %d ", a);

//but if the user enters something inappropriate, like a character, the program leads to 
//undetermined behavior (which as I understand it interprets said character according to its 
//value in the ASCII code).

From already thank you very much从已经非常感谢

How to get verified user input of a specific type如何获得特定类型的经过验证的用户输入

#1 Get user input as a string #1 以字符串形式获取用户输入

char s[100];
if (!fgets( s, sizeof(s), stdin )) *s = '\0';
char * p = strptok( s, "\r\n" );
if (!p) complain_and_quit();
*p = '\0';

...

Alternately:交替:

#define __STDC_WANT_LIB_EXT2__ 1
#include <stdio.h>

char * s = NULL;
size_t n = 0;
if (getline( &s, &n, stdin ) < 0)
{
  free( s );
  complain_and_quit();
}

...

free( s );

#2 Try to convert that string to the type of thing you want. #2 尝试将该字符串转换为您想要的类型。

char * p;
int user_input = strtol( s, &p, 10 );
if (*p)
{
  // Input was not JUST an integer.
  // It could be something like "123 xyz", or "not-an-integer".
  // Look at the value of p to figure out where the conversion went wrong.
  complain();
}

do_something_with_an_integer( user_input );

That's it!而已!

The best option is to use fgets to parse the input, it reads the input stream as a string, which gives you more options to deal with bad input, and you already have some suggestions on how to do it.最好的选择是使用fgets来解析输入,它将输入 ZF7B44CFFAFD5C52223D5498196C8A2E7BZ 作为字符串读取,这为您提供了更多处理错误输入的选项,并且您已经对如何处理有一些建议。 In any case, if you are adamant in using scanf , you could do something like this:无论如何,如果你坚持使用scanf ,你可以这样做:

//...
int a;
printf("Enter a number\n");
while (scanf("%d", &a) == 0) //while input is not correctly parsed...
{
    fprintf(stderr, "ERROR, non numeric value is not allowed\n"); //...print error message...
    int c;
    while((c = getchar()) != '\n' && c != EOF) {} //...and clear stdin
}
printf("Your number is %d ", a);
//...

The above code will keep prompting the user to get an input until a valid one is provided.上面的代码将不断提示用户获取输入,直到提供有效的输入。

In order to determine whether scanf was able to successfully convert the input to an integer, you should check the return value of scanf :为了确定scanf是否能够成功地将输入转换为 integer,您应该检查scanf的返回值:

#include <stdio.h>
#include <stdlib.h>

int main( void )
{
    int num;

    printf( "Enter a number: " );
    if ( scanf( "%d", &num ) != 1 )
    {
        printf( "Failed to convert input!\n" );
        exit( EXIT_FAILURE );
    }

    printf( "Conversion successful! The number is %d.\n", num );
}

However, using scanf for line-based user input is generally not recommended, because scanf does not behave in an intuitive manner when dealing with that kind of input.但是,通常不建议将scanf用于基于行的用户输入,因为在处理此类输入时, scanf的行为方式并不直观。 For example, scanf will generally not consume an entire line of input at once.例如, scanf通常不会一次消耗一整行输入。 Instead, it will generally only consume the input that matches the argument, but will leave the rest of the line on the input stream, including the newline character.相反,它通常只会消耗与参数匹配的输入,但会在输入 stream 上留下该行的 rest,包括换行符。

Leaving the newline character on the input stream can already cause a lot of trouble.在输入 stream 上留下换行符已经会造成很多麻烦。 For example, see this question .例如,看到这个问题

Also, if the user enters for example 6abc , then scanf will successfully match the 6 and report success, but leave abc on the input stream, so that the next call to scanf will probably immediately fail.此外,如果用户输入例如6abc ,那么scanf将成功匹配6并报告成功,但将abc留在输入 stream 上,这样下一次对scanf的调用可能会立即失败。

For this reason, it is generally better to always read one line of input at a time, using the function fgets .出于这个原因,通常最好一次读取一行输入,使用 function fgets After successfully reading one line of input as a string, you can use the function strtol to attempt to convert the string to an integer:成功将一行输入读取为字符串后,您可以使用 function strtol尝试将字符串转换为 integer:

#include <stdio.h>
#include <stdlib.h>

int main( void )
{
    char line[200], *p;
    int num;

    //prompt user for input
    printf( "Enter a number: " );

    //attempt to read one line of input
    if ( fgets( line, sizeof line, stdin ) == NULL )
    {
        printf( "Input failure!\n" );
        exit( EXIT_FAILURE );
    }

    //attempt to convert strint to integer
    num = strtol( line, &p, 10 );
    if ( p == line )
    {
        printf( "Unable to convert to integer!\n" );
        exit( EXIT_FAILURE );
    }

    //print result
    printf( "Conversion successful! The number is %d.\n", num );
}

However, this code has the following issues:但是,此代码存在以下问题:

  1. It does not check whether the input line was too long to fit into the buffer.它不检查输入行是否太长而无法放入缓冲区。

  2. It does not check whether the converted number is representable as an int , ie whether the value the user entered is too large to be stored in an int .它不检查转换后的数字是否可以表示为int ,即用户输入的值是否太大而无法存储在int中。

  3. It will accept 6abc as valid input for the number 6 .它将接受6abc作为数字6的有效输入。 This is not as bad as scanf , because scanf will leave abc on the input stream, whereas fgets will not.这不如scanf糟糕,因为scanf会将abc留在输入 stream 上,而fgets不会。 However, it would probably still be better to reject the input instead of accepting it.但是,拒绝输入而不是接受它可能仍然会更好。

Here is an improved version of the code, which solves the issues mentioned above and also puts everything into a function.这是代码的改进版本,它解决了上述问题,并将所有内容放入 function 中。 This function will reprompt the user for input, until the input is valid.此 function 将重新提示用户输入,直到输入有效。

#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 )
{
    //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 "6abc" 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;
    }
}

int main( void )
{
    int number;

    number = get_int_from_user( "Enter a number: " );

    printf( "Input was valid.\n" );
    printf( "The number is: %d\n", number );

    return 0;
}

This program has the following behavior:该程序具有以下行为:

Enter a number: abc
Error converting string to number!
Enter a number: 6000000000
Number out of range error!
Enter a number: 6 7 8
Unexpected input encountered!
Enter a number: 6abc
Unexpected input encountered!
Enter a number: 6
Input was valid.
The number is: 6

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM