如何仅使用scanf读取整数,并在用户输入非数字字符时重复读取?

15

以下是一些C代码,旨在防止用户输入小于0或大于23的字符或整数。

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

int main(void)
{
    const char *input;
    char *iPtr;
    int count = 0;
    int rows;

    printf("Enter an integer: ");
    scanf("%s", input);
    rows = strtol(input, &iPtr, 0);
    while( *iPtr != '\0') // Check if any character has been inserted
    {
        printf("Enter an integer between 1 and 23: ");
        scanf("%s", input);
    }
    while(0 < rows && rows < 24) // check if the user input is within the boundaries
    {
        printf("Select an integer from 1 to 23: ");
        scanf("%s", input);
    }  
    while (count != rows)  
    {  
        /* Do some stuff */  
    }  
    return 0;  
}

我已经完成了一半,需要一点鼓励来推动我继续前行。


请移除 while (count != rows);。这会阻塞你的程序。 - MOHAMED
请输入一个1到23之间的整数:123asd(无效),erwea(无效),-823(无效),30(无效),8(有效) - 7kemZmani
@AbdulelahAl-Jeffery:我更新了我的答案中的代码,并进行了测试,它可以正常工作。 - MOHAMED
"%[^\n]" 的意思是:期望输入一个字符串,其中包含除 "\n"(换行符)以外的任何字符。这意味着读取和清除标准输入的全部内容(感谢真主)。 - MOHAMED
@MohamedKALLEL 好的,现在我明白了。我注意到你把 input 数组去掉了,用 char 类型的 c 替换了它?!这是因为我们想尽可能地节省内存吗? - 7kemZmani
显示剩余12条评论
6个回答

39
使用scanf("%d",&rows)而不是scanf("%s",input) 这样可以直接从标准输入中获取整数值,无需转换为int类型。
如果用户输入的字符串包含非数字字符,则在下一个scanf("%d",&rows)之前需要清除标准输入。
你的代码可能如下所示:
#include <stdio.h>  
#include <stdlib.h> 

int clean_stdin()
{
    int c;
    while ((c = getchar()) != '\n' && c != EOF)
        ;
    return 1;
}

int main(void)  
{ 
    int rows =0;  
    char c;
    do
    {  
        printf("\nEnter an integer from 1 to 23: ");
  
    } while (((scanf("%d%c", &rows, &c)!=2 || c!='\n') && clean_stdin()) || rows<1 || rows>23);

    return 0;  
}

解释

1)

scanf("%d%c", &rows, &c)

这意味着期望用户输入一个整数,并紧跟一个非数字字符。

示例1:如果用户输入aaddk,然后ENTER,scanf将返回0。没有捕获到任何内容

示例2:如果用户输入45,然后ENTER,scanf将返回2(捕获了2个元素)。这里%d捕获了45%c捕获了\n

示例3:如果用户输入45aaadd,然后ENTER,scanf将返回2(捕获了2个元素)。这里%d捕获了45%c捕获了a

2)

(scanf("%d%c", &rows, &c)!=2 || c!='\n')

在示例1中:这个条件是TRUE,因为scanf返回0(!=2)
在示例2中:这个条件是FALSE,因为scanf返回2并且c == '\n'
在示例3中:这个条件是TRUE,因为scanf返回2并且c == 'a'(!='\n')
3)
((scanf("%d%c", &rows, &c)!=2 || c!='\n') && clean_stdin())

clean_stdin()始终为TRUE,因为该函数始终返回1

在示例1中: (scanf("%d%c", &rows, &c)!=2 || c!='\n')TRUE,因此需要检查&&后面的条件,因此将执行clean_stdin(),整个条件为TRUE

在示例2中: (scanf("%d%c", &rows, &c)!=2 || c!='\n')FALSE,因此不会检查&&后面的条件(无论其结果如何,整个条件都将为FALSE),因此不会执行clean_stdin(),整个条件为FALSE

在示例3中:条件(scanf("%d%c", &rows, &c)!=2 || c!='\n')TRUE,所以在&&之后的条件应该被检查,这样clean_stdin()将被执行,并且整个条件为TRUE
因此,你可以注意到只有当用户输入包含非数字字符的字符串时,clean_stdin()才会被执行。
而且,这个条件((scanf("%d%c", &rows, &c)!=2 || c!='\n') && clean_stdin())只有当用户输入一个整数而没有其他内容时,才会返回FALSE
如果条件((scanf("%d%c", &rows, &c)!=2 || c!='\n') && clean_stdin())FALSE,且整数在123之间,则while循环将会终止;否则while循环将会继续。

首先,没有检查scanf()是否实际填充了“rows”的任何内容(如果有的话)。因此,您可能会检查一个不确定的值。您至少应该验证scanf()返回了1。 - WhozCraig
وˆ‘ن½؟用ن؛†clean_stdinه‡½و•°ن»£و›؟scanf("%[^\n]",input),ن»¥éپ؟ه…چinputçڑ„缓ه†²هŒ؛و؛¢ه‡؛م€‚ - MOHAMED
当用户只按下回车键时,您能否使其重复? - nyxaria

3
#include <stdio.h>
main()
{
    char str[100];
    int num;
    while(1) {
        printf("Enter a number: ");
        scanf("%[^0-9]%d",str,&num);
        printf("You entered the number %d\n",num);
    }
    return 0;
}

%[^0-9]scanf()中会将非09之间的所有字符都过滤掉,只将数字保存在str中。需要注意的是,非数字序列的长度最长只能为100。接下来的%d则会从输入流中选择整数,并将其保存在num中。


1
你可以创建一个函数,读取介于1到23之间的整数,如果不是整数则返回0。
例如:
int getInt()
{
  int n = 0;
  char buffer[128];
  fgets(buffer,sizeof(buffer),stdin);
  n = atoi(buffer); 
  return ( n > 23 || n < 1 ) ? 0 : n;
}

字符怎么办?我不希望用户输入超出范围的字符串或整数。 - 7kemZmani
始终不要对用户所写的内容做任何假设,这是非常好的做法。 - AndersK

1
char check1[10], check2[10];
int foo;

do{
  printf(">> ");
  scanf(" %s", check1);
  foo = strtol(check1, NULL, 10); // convert the string to decimal number
  sprintf(check2, "%d", foo); // re-convert "foo" to string for comparison
} while (!(strcmp(check1, check2) == 0 && 0 < foo && foo < 24)); // repeat if the input is not number

如果输入是数字,你可以将foo用作输入。

0

在你要求用户重试的循环中,你需要重复调用strtol。实际上,如果你将循环改为do { ... } while(...);而不是while,你就不会得到重复两次的行为。

你还应该格式化你的代码,以便能够看出代码在循环内还是外。


-1

这是针对防止用户输入错误的数据类型的回复。

如果你只是处理数字,请声明

char catchpan;

然后在输入数字的scanf之后,放置

scanf("%c", &catchpan);

虽然这不是完美的解决方案,但如果你有处理越界问题的代码(假设零不是边界的一部分),它就会很清晰。

这不是完美的,也不是通用的解决方案。


那么你是在另一个问题上回答一个已经关闭的问题?这样做 - 嗯 - 简直不是正确的方式。 :) - klutt
这并没有回答问题。一旦您拥有足够的声望,您将能够评论任何帖子;相反,提供不需要询问者澄清的答案。- 来自审核 - lee-m

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