【问题标题】:How to scanf only integer and repeat reading if the user enters non-numeric characters?如果用户输入非数字字符,如何仅扫描整数并重复阅读?
【发布时间】:2012-12-31 08:47:04
【问题描述】:

这里有一些 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;  
}

我做到了一半,一个小小的俯卧撑将不胜感激。

【问题讨论】:

  • 我注意到第三个 scanf 命令有一个明显的问题,应该是 scanf("%i", &rows);但代码仍然被破坏:(
  • 您是否考虑过为那些scanf 调用分配内存?就目前而言,他们正在读取保存在未初始化指针 (input) 中的地址,这是未定义的行为。我很确定它的int 值是否应该使用%d 并扫描到int 变量的地址。此外,检查您的scanf 调用的返回值,它会告诉您成功获得了多少字段。
  • 我把'input'指针改成了数组'char input[100];'
  • 你为什么要将它读入文本缓冲区根本?您正在寻找[0..23] 中的整数值,对吗?只需扫描到int 并检查是否成功解析和范围内的值,除非您也有兴趣获取一些特殊字符。也许有必要阅读更多关于scanf() 的信息?
  • 我不希望用户输入两种类型的输入,字符和超出范围的整数,这就是我使用文本缓冲区的原因。

标签: c validation user-input


【解决方案1】:

使用scanf("%d",&amp;rows) 而不是scanf("%s",input)

这允许您直接从标准输入获取整数值,而无需转换为 int。

如果用户输入了一个包含非数字字符的字符串,那么你必须在下一个scanf("%d",&amp;rows) 之前清理你的标准输入。

您的代码可能如下所示:

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

int clean_stdin()
{
    while (getchar()!='\n');
    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)

这意味着期望用户输入一个整数并在其附近输入一个非数字字符。

Example1:如果用户输入aaddk,然后输入ENTER,scanf 将返回0。没有任何标题

Example2:如果用户输入45,然后输入ENTER,scanf 将返回2(2 个元素被加盖)。这里%d45%c\n

Example3:如果用户输入45aaadd,然后输入ENTER,scanf 将返回2(2 个元素被加盖)。这里%d45%ca

2)

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

在例子1中:这个条件是TRUE,因为scanf返回0(!=2)

在例子2中:这个条件是FALSE,因为scanf返回2c == '\n'

在例子3中:这个条件是TRUE,因为scanf返回2c == 'a' (!='\n')

3)

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

clean_stdin() 总是 TRUE 因为函数总是返回 1

在示例 1 中: (scanf("%d%c", &amp;rows, &amp;c)!=2 || c!='\n')TRUE,因此应检查 &amp;&amp; 之后的条件,以便执行 clean_stdin() 并且整个条件是TRUE

在示例 2 中: (scanf("%d%c", &amp;rows, &amp;c)!=2 || c!='\n')FALSE,因此不会检查 &amp;&amp; 之后的条件(因为它的结果就是整个条件将是 FALSE ) 所以 clean_stdin() 不会被执行,整个条件是 FALSE

在示例 3 中: (scanf("%d%c", &amp;rows, &amp;c)!=2 || c!='\n')TRUE,因此应检查 &amp;&amp; 之后的条件,以便执行 clean_stdin() 并且整个条件是TRUE

所以你可以说clean_stdin()只有当用户输入一个包含非数字字符的字符串时才会被执行。

只有当用户输入 integer 而没有其他内容时,此条件 ((scanf("%d%c", &amp;rows, &amp;c)!=2 || c!='\n') &amp;&amp; clean_stdin()) 才会返回 FALSE

如果条件((scanf("%d%c", &amp;rows, &amp;c)!=2 || c!='\n') &amp;&amp; clean_stdin())FALSE 并且integer 介于123 之间,那么while 循环将中断,否则while 循环将继续

【讨论】:

  • 对于初学者来说,没有检查scanf() 所做的(如果有的话)是否实际填充了rows。因此,您可能正在检查一个不确定的值。您至少应该验证 scanf() 返回 1。
  • 我使用clean_stdin函数代替scanf("%[^\n]",input)以避免input的缓冲区溢出
  • 当用户只按回车时,你能不能让这个也重复?
【解决方案2】:
#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] in scanf() 吞噬了所有不在09 之间的内容。基本上它会清除非数字的输入流并将其放入str。嗯,非数字序列的长度限制为100。下面的%d只选择输入流中的整数,放在num中。

【讨论】:

    【解决方案3】:

    您可以创建一个读取 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;
    }
    

    【讨论】:

    • 字符呢?我不希望用户输入超出范围的字符串或整数。
    • 最好不要假设用户所写的内容。
    【解决方案4】:
    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作为你的输入。

    【讨论】:

      【解决方案5】:

      您需要在您要求用户重试的循环中重复调用strtol。事实上,如果你将循环设为do { ... } while(...); 而不是while,你不会得到相同类型的重复事情两次的行为。

      您还应该格式化您的代码,以便可以看到代码在循环内的位置,而不是。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-08-04
        • 2014-12-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-05-11
        • 1970-01-01
        相关资源
        最近更新 更多