如何只扫描整数?

问题描述 投票:0回答:9

我希望代码一直运行,直到用户输入整数值。

该代码适用于

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

如何避免这种情况?

c scanf
9个回答
45
投票
  1. 你拿
    scanf()
  2. 你把它扔进垃圾桶。
  3. 您可以使用
    fgets()
    来获取整行。
  4. 您使用
    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));

15
投票

使用

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;
}

9
投票

如果您决定使用

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
}

6
投票

尝试在

scanf
中使用以下模式。它将一直读到行尾:

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

循环内不需要

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


4
投票

我知道如何使用

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
转换对返回值的影响做出任何假设。


4
投票

一个可能的解决方案是向后思考:接受浮点数作为输入,如果浮点数不是整数则拒绝输入:

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

虽然这确实允许用户输入 12.0 或 12e0 等内容。


3
投票

使用

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


2
投票

虽然通常不建议使用

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
解决方案存在以下问题:

  1. 如果用户输入无法表示为 int 的数字(例如大于
    INT_MAX
     的数字),程序将出现 
    未定义的行为
  2. 如果用户输入空行,则不会打印错误消息。这是因为
    %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

0
投票

也许不是最佳解决方案..但仅使用 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;
}
© www.soinside.com 2019 - 2024. All rights reserved.