在C语言中检查输入是否为整数类型

44

问题在于我不能使用atoi或其他类似的函数(我相信我们应该依赖数学运算)。

 int num; 
 scanf("%d",&num);
 if(/* num is not integer */) {
  printf("enter integer");
  return;
 }

我尝试过:

(num*2)/2 == num
num%1==0
if(scanf("%d",&num)!=1)

但是这些都没有起作用。

有什么想法吗?


你说的“这些都没用”是什么意思?你最后一个尝试了吗? - David Thornley
@David 我将它们放在if语句中了,这有什么困惑的地方吗? - Gal
“整数类型”是什么样的输入?只包含数字的输入吗?负号可以放在前面吗? - eq-
你列出了两个条件和一个if语句。我猜你没有在if语句中包含if语句,所以我不知道你在那里做了什么。(你只是替换了if语句,还是更重要的事情?)你也没有提供任何关于失败模式的线索,除了“这些都没有起作用”。是编译错误吗?运行时错误?没有按照你的预期工作(如果是这样,你期望什么?)? - David Thornley
@Gal,你是在问如果有人输入5.5,然后得到了5这个结果吗?因为我不确定是否可以检查这种情况,如果可以的话,我想知道如何检查。 - eran otzap
1
我知道我来晚了,但我想你“不能”(意思是:不允许)使用atoi(),因为这个想法是你通过遍历字符并检查它们是否在0-9范围内来手动进行检查。因此,很可能也不应该使用scanf() - Peter - Reinstate Monica
16个回答

56

num将始终包含一个整数,因为它是一个int。您代码中真正的问题在于您没有检查scanf的返回值。scanf 返回成功读取的项目数量,在这种情况下,它必须返回1个有效值。如果没有,那么输入的整数值无效,而且变量num可能没有被更改(即仍具有任意值,因为您没有初始化它)。

根据您的评论,您只想让用户输入一个整数,然后再按回车键。不幸的是,这不能简单地通过scanf("%d\n")来实现,但是以下是一个技巧:

int num;
char term;
if(scanf("%d%c", &num, &term) != 2 || term != '\n')
    printf("failure\n");
else
    printf("valid integer followed by enter key\n");

不,我检查了返回值,即使是像“2k”这样的非整数也是“1”。 - Gal
scanf 正在读取一个数字并检测到一个非数字字符时,它只会停在那里并将其留给流(在这种情况下,只会简单地“返回”2)。 - eq-
@eq无关紧要,如果用户插入了一个非整数键,我必须检测并打印错误。 - Gal
@sombe:已编辑以允许您的使用情况。 - AndiDog
Pedantic:scanf("%d%c", &num, &term) 返回>= 1就足以知道是否输入了一个int。换句话说,输入可能只有123文件结束符 - chux - Reinstate Monica
显示剩余2条评论

28
你需要先将输入读取为字符串,然后解析该字符串以查看它是否包含有效的数字字符。如果是,则可以将其转换为整数。
char s[MAX_LINE];

valid = FALSE;
fgets(s, sizeof(s), stdin);
len = strlen(s);
while (len > 0 && isspace(s[len - 1]))
    len--;     // strip trailing newline or other white space
if (len > 0)
{
    valid = TRUE;
    for (i = 0; i < len; ++i)
    {
        if (!isdigit(s[i]))
        {
            valid = FALSE;
            break;
        }
    }
}

有没有一种使用正则表达式实现这个的方法? - Gal
@delnan,那就是我所问的。 - Gal
14
请不要建议使用危险的 gets() 函数。 - Greg Hewgill
2
@sombe,你确实需要这样做,但在检查它是否有效后,你需要添加一个sscanf(s, "%d", num);,它将把它转换为整数。 - user470379
2
@sombe,你能告诉我们实际的要求吗?我猜你应该也需要处理字符串转十进制的转换(这就是我会写的任务)。 - The Archetypal Paul
显示剩余3条评论

21

使用 %d 转换说明符配合 scanf 做此操作存在以下几个问题:

  1. 如果输入字符串以有效的整数开头(如 "12abc"),那么 "12" 将从输入流中读取并被转换和赋值给 numscanf 将返回 1,因此你会在(可能)不应该的情况下指示成功;

  2. 如果输入字符串不是以数字开头,则 scanf 不会从输入流中读取任何字符,num 不会改变,返回值为 0;

  3. 你没有指定是否需要处理非十进制格式,但如果必须处理八进制或十六进制格式的整数值(如 0x1a),则这种方法行不通。 %i 转换说明符可处理十进制、八进制和十六进制格式,但前两个问题仍然存在。

首先,你需要将输入作为字符串读入(最好使用 fgets)。如果不允许使用 atoi,那么也可能不允许使用 strtol。因此,你需要检查字符串中的每个字符。安全地检查数字值的方法是使用 isdigit 库函数(还有用于检查八进制和十六进制数字的 isodigitisxdigit 函数),例如:

while (*input && isdigit(*input))
   input++;    
(如果你甚至不允许使用 isdigitisodigitisxdigit,那么就应该责骂你的老师或教授,因为这让任务变得比实际需要的更加困难。)
如果需要处理八进制或十六进制格式,那么情况会稍微复杂一些。在 C 语言中,八进制格式以前导数字 0 开头,十六进制格式则以前导数字 0x 开头。因此,如果第一个非空白字符是 0,则必须检查下一个字符才能确定要使用哪种非十进制格式。
基本大纲如下:
  1. 如果第一个非空白字符不是 '-'、'+'、'0' 或非零十进制数字,则这不是一个有效的整数字符串;
  2. 如果第一个非空白字符是 '-',则这是一个负值,否则我们假设是正值;
  3. 如果第一个字符是 '+',则这是一个正值;
  4. 如果第一个非空白字符是非零十进制数字,则输入是以十进制格式表示的,您将使用 isdigit 检查剩余字符;
  5. 如果第一个非空白字符是 '0',则输入是以八进制或十六进制格式表示的;
  6. 如果第一个非空白字符是 '0',且下一个字符是从 '0' 到 '7' 的数字,则输入是以八进制格式表示的,您将使用 isodigit 检查剩余字符;
  7. 如果第一个非空白字符是 0,且第二个字符是 xX,则输入是以十六进制格式表示的,您将使用 isxdigit 检查剩余字符;
  8. 如果剩余字符中有任何一个不满足上述检查函数的条件,则这不是一个有效的整数字符串。

4

首先请问您如何预料这段代码不返回整数:

int num; 
scanf("%d",&num);

你将变量指定为整数类型,然后使用scanf函数,但是只为整数(%d)赋值。
在这一点上它可能包含什么其他内容呢?

抱歉,很多人都对这个答案感到狂热,但是我从算法中预期的正是你所写的,但它没有起作用。我应该检查值,以便它永远不是非整数,而你的解决方案不能满足要求。我有什么遗漏的吗?请解释一下。 - Gal
1
@sombe:你没有考虑到的是,scanf("%d",&num) 只会根据定义将整数值存储到 num 中;无论这个值与输入字符串是否有任何关系都是一个未解决的问题。如果你输入了一些带有前导数字的内容(比如 "12efg"),scanf 会读取、转换和赋值前导的 "12",而将 "efg" 留在输入流中。如果输入字符串不以数字开头(例如 "ab123"),则不会读取输入字符串的任何部分,而 num 将包含调用之前的值(仍然是一个整数)。 - John Bode
“在这一点上它可能包含什么其他东西呢?” --> 一个陷阱值 - 虽然我多年没有看到 int 使用陷阱了。 - chux - Reinstate Monica

1
如果其他人遇到这个问题,我已经编写了一个程序,它会不断要求输入数字,如果用户的输入不是整数,则继续要求输入,直到接受整数为止。
#include<stdlib.h>
#include<stdio.h>
#include<stdbool.h>

bool digit_check(char key[])
{
    for(int i = 0; i < strlen(key); i++)
    {
        if(isdigit(key[i])==0)
        {
            return false;
        }
    }
    return true;
}

void main()
{
    char stroka[10];
    do{
        printf("Input a number: ");
        scanf("%s",stroka);}
    while (!digit_check(stroka));
    printf("Number is accepted, input finished!\n");
    system("pause");
}

0

我想这个更加用户友好:

#include<stdio.h>

/* This program checks if the entered input is an integer
 * or provides an option for the user to re-enter.
 */

int getint()
{
  int x;
  char c;
  printf("\nEnter an integer (say -1 or 26 or so ): ");
  while( scanf("%d",&x) != 1 )
  {
    c=getchar();

    printf("You have entered ");
    putchar(c);
    printf(" in the input which is not an integer");

    while ( getchar() != '\n' )
     ; //wasting the buffer till the next new line

    printf("\nEnter an integer (say -1 or 26 or so ): ");

  }

return x;
}


int main(void)
{
  int x;
  x=getint();

  printf("Main Function =>\n");
  printf("Integer : %d\n",x);

 return 0;
}

while( scanf("%d",&x) != 1 ) loop is infinite loop should stdin get closed as scanf("%d",&x) will forever return EOF. As well while ( getchar() != '\n' ) - chux - Reinstate Monica

0

我使用gets开发了这个逻辑,避免了scanf的麻烦:

void readValidateInput() {

    char str[10] = { '\0' };

    readStdin: fgets(str, 10, stdin);
    //printf("fgets is returning %s\n", str);

    int numerical = 1;
    int i = 0;

    for (i = 0; i < 10; i++) {
        //printf("Digit at str[%d] is %c\n", i, str[i]);
        //printf("numerical = %d\n", numerical);
        if (isdigit(str[i]) == 0) {
            if (str[i] == '\n')break;
            numerical = 0;
            //printf("numerical changed= %d\n", numerical);
            break;
        }
    }
    if (!numerical) {
        printf("This is not a valid number of tasks, you need to enter at least 1 task\n");
        goto readStdin;
    }
    else if (str[i] == '\n') {
        str[i] = '\0';
        numOfTasks = atoi(str);
        //printf("Captured Number of tasks from stdin is %d\n", numOfTasks);
    }
}

2
str[10] 对于大的 int 值是不足够的。 - chux - Reinstate Monica

0
我解决这个问题的方法是使用cs50.h库。因此,头文件如下:
#include <cs50.h>

这里有一个 get_int 函数,你可以直接用它来初始化变量:

    int a = get_int("Your number is: "); 

如果用户输入的不是整数,则输出重复显示“您的数字是:”这一行,直到输入整数为止。

0
我仔细阅读了上面所有人的输入,这非常有用,于是我编写了一个适合自己应用的函数。实际上,该函数只是评估用户的输入是否为“0”,但对于我的目的来说已经足够好了。希望这会有所帮助!
#include<stdio.h>

int iFunctErrorCheck(int iLowerBound, int iUpperBound){

int iUserInput=0;
while (iUserInput==0){
    scanf("%i", &iUserInput);
    if (iUserInput==0){
        printf("Please enter an integer (%i-%i).\n", iLowerBound, iUpperBound);
        getchar();
    }
    if ((iUserInput!=0) && (iUserInput<iLowerBound || iUserInput>iUpperBound)){
        printf("Please make a valid selection (%i-%i).\n", iLowerBound, iUpperBound);
        iUserInput=0;
    }
}
return iUserInput;
}

代码应该在使用 iUserInput 之前检查 scanf("%i", &iUserInput) 的返回值。 - chux - Reinstate Monica

0

试一试...

#include <stdio.h>

int main (void)
{
    float a;
    int q;

    printf("\nInsert number\t");
    scanf("%f",&a);

    q=(int)a;
    ++q;

    if((q - a) != 1)
        printf("\nThe number is not an integer\n\n");
    else
        printf("\nThe number is an integer\n\n");

    return 0;
}

1
scanf("%f",&a) 的输入为无效数值且 a 未初始化的情况下,q=(int)a; 是未定义的。代码应该在使用 a 之前检查 scanf("%f",&a) 的返回值。 - chux - Reinstate Monica

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接