C语言 如何只扫描整数?

xoefb8l8  于 2022-12-29  发布在  其他
关注(0)|答案(9)|浏览(196)

我希望代码一直运行到用户输入一个整数值。
该代码适用于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

如何才能避免这种情况?

am46iovg

am46iovg1#

1.你拿scanf()
1.你把它扔到垃圾桶里。
1.使用fgets()可以得到一整行。
1.使用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));
nkoocmlb

nkoocmlb2#

使用fgetsstrtol
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;
}
agxfikkp

agxfikkp3#

如果您打算使用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
}
q5iwbnjs

q5iwbnjs4#

尝试在scanf中使用以下模式,它将读取到行尾:

scanf("%d\n", &n)

循环中不需要getchar(),因为scanf将读取整行,浮点数将与scanf模式不匹配,提示符将再次要求输入整数。

vsikbqxv

vsikbqxv5#

我知道如何使用fgetsstrtol完成此操作,我想知道如何使用scanf()完成此操作(如果可能)。
正如其他答案所说,scanf并不真正适合于此,fgetsstrtol是一种替代方案(尽管fgets有一个缺点,即很难检测输入中的0字节,并且不可能判断0字节之后输入了什么,如果有的话)。
为了完整起见(并假设有效输入是一个整数,后跟一个换行符):

while(scanf("%d%1[\n]", &n, (char [2]){ 0 }) < 2)

或者,在%*1[\n]之前和之后使用%n,并隐藏赋值。但请注意(来自Debian manpage):
这不是一个转换,尽管可以使用*赋值隐藏字符隐藏它。C标准规定:“执行%n指令不会增加执行完成时返回的赋值计数”,但勘误表似乎与此相矛盾。也许明智的做法是不要对%n转换对返回值的影响做任何假设。

aij0ehis

aij0ehis6#

一个可能的解决办法是倒着想:接受浮点数作为输入,如果浮点数不是整数,则拒绝输入:

int n;
float f;
printf("Please enter an integer: ");
while(scanf("%f",&f)!=1 || (int)f != f)
{
    ...
}
n = f;

尽管这确实允许用户输入12.0或12e0等。

g9icjywg

g9icjywg7#

使用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),此方法不会重新提示。

qpgpyjmq

qpgpyjmq8#

可能不是最佳解决方案..但仅使用 scanfstrtol

#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;
}
8mmmxcuj

8mmmxcuj9#

尽管通常不建议将scanf用于基于行的用户输入,但我将首先介绍一个使用scanf的解决方案。
此解决方案的工作原理是检查行中未被scanf使用的剩余字符。通常,这应该仅是换行符。但是,由于将%d说明符与scanf一起使用时接受前导whitespace characters,因此,如果程序也接受尾随空格字符,则会保持一致。
下面是我使用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解决方案存在以下问题:
1.如果用户输入的数字不能表示为int(例如大于INT_MAX的数字),则程序将具有undefined behavior
1.如果用户输入一个空行,它不会打印错误消息,这是因为scanf%d说明符使用了所有前导空格字符。
这两个问题可以通过使用函数fgetsstrtol而不是函数scanf来解决。
但是,执行所有这些验证检查会使代码变得相当大。因此,将所有代码放入其自己的函数中是有意义的。下面是一个使用函数get_int_from_user的示例,该函数取自answer of mine to another question

#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

相关问题