【问题标题】:How to not count 1.0 as an integer如何不将 1.0 计为整数
【发布时间】:2022-01-16 01:00:47
【问题描述】:

所以我需要从用户可以输入 1.0 的标准输入中读取一个整数,但是由于这是一个双精度数,我不想接受它。但是,当我尝试下面的方法时 1.0 被转换为 1 并被接受。我也想接受 0001 作为可能的整数输入 1。

    first_sentence_to_switch = 0;
    char buf[15]; // large enough
    int number;
    wrong_input = 0;

    scanf("%14s", buf); // read everything we have in stdin
    // printf("buffer: %s", buf);
    if (sscanf(buf, "%d", &number) == 1)
    {
      first_sentence_to_switch = number;
    }
    else
    {
      wrong_input = 1;
    }

【问题讨论】:

  • 提示:strtol() 允许您进行错误检查。
  • 也许用strchr(buff, '.')检查字符串?虽然这可能会导致某些输入出现问题。
  • 我不能使用 string.h @alex01011
  • @chrisbasmaci 你可以自己实现它:) 看看manpage
  • 函数strtol()stdlib.h中。

标签: c scanf stdin


【解决方案1】:

您可以使用 %n 格式选项来判断 sscanf 调用匹配了多少,以确保该行没有多余的内容:

if (sscanf(buf, "%d %n", &number, &end) == 1 && buf[end] == 0) {
    .. ok
} else {
    .. not an integer or something else in the input (besides whitespace) after the integer

注意%d%n 之间的空格以跳过缓冲区末尾可能存在的任何空格(例如,如果输入由 fgets 或 getline 读取,则为换行符)

【讨论】:

  • 快速且切中要害。可能值得一提的是"%d %n" 之间的空格可以优雅地处理您在else 下解释的空格。 (对于新的 C 程序员来说,欣赏这种联系可能有点微妙)
  • 使用sscanf 解决这个问题的缺点是,如果用户输入的数字太高以至于无法表示为int (see §7.21.6.2 ¶10 of the ISO C11 standard) .因此,在我看来,函数sscanf 通常不应该用于输入验证。另一方面,函数strtol 可以可靠地处理和报告这种错误情况。不过,我仍然支持这个答案,因为它是解决 OP 眼前问题的最简单方法。
【解决方案2】:

如何读取整行输入

线

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将字符串转换为整数

在将输入行读入内存缓冲区后,您可以使用函数sscanfstrtol 尝试将整数转换为数字。我建议您使用函数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 );
}

fgetsstrtol的代码示例

#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!

【讨论】:

    【解决方案3】:

    由于可能存在大量错误输入,您应该只寻找正确的输入:'1''0'

    '我也愿意接受 0001 ...'

    我只是从你的解释中假设你不想接受某事 喜欢:0011

    我会从缓冲区的末尾看向开头。 换句话说: 我只在缓冲区末尾寻找单个 '1',然后 only 寻找 '0'(零) 直到到达buf 的开头。

    其他都是错误的输入

    由于您随意选择缓冲区大小,您可以编写如下内容:

    #define BUFF_SZ 15
       ...
    char buf[BUFF_SZ];
       ...
    while (buf[++i]); // <-- to avoid measuring buffer size at runtime.
    

    这是一个带有返回正确结果的函数的代码示例:

    #include <stdio.h>
    
    int check_input (char *buf);    
    
    int main()
    {
       char buf[15]; // large enough
    
        scanf("%14s", buf);
        if (check_input(buf) == 0) {  printf("Wrong input!"); return(1); };
    
           ... input OK ...
    
        return (0);
    }
    
    // function returns:    1: on success,  0: on wrong input
    int check_input (char *buf)
    {
      int i=0;
    
        while (buf[++i]);   // it will stop counting when NULL char is found ..
                            // so it's like 'strlen(buff)' 
                            // without unnecessary including <string.h>
     
        // buffer is set at end so check if it ends with '1' ..                  
        if (buf[--i] != '1')     return (0);
        
        // now, check all buffer backwards to make sure that ALL of those are '0's..
        while ((--i) > 0) 
            if (buf[i] != '0')    return (0);
    
        return (1);
    }
    

    我将最重要的部分写成一个函数,这样它会更易读。

    希望对你有帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-04-22
      • 1970-01-01
      • 2022-09-30
      • 2011-04-04
      • 2015-10-16
      • 2021-05-29
      相关资源
      最近更新 更多