【问题标题】:how to get program to accept only positive integer values in c如何让程序在c中只接受正整数值
【发布时间】:2018-10-11 23:30:09
【问题描述】:

编写一个程序来查找用户输入的最小值、最大值、平均值。编写将检查以确保仅输入正整数并产生错误消息的内容时遇到问题。这是我目前正在读取输入的 for 语句:

  for (int value = 0; value <= numofvals; ++value) {
      printf("Value %d: %f\n", value, val_input);
      scanf("%f", &val_input);
  }

请注意,我已经学习代码大约 3 周了,这周刚刚介绍了循环,所以我的理解充其量只是初步的!

【问题讨论】:

  • 如何打印从stdin 读取的val_input 行*在您的printf() 之后*?
  • 您需要在读入值后使用 if-then 语句来验证输入是否符合您(更重要的是,您的程序)所期望的。这是您最终会在编程中花费大量工作的事情,因为无效输入可能会导致严重的安全问题和其他错误。基本规则是从不信任用户输入并始终验证所有内容。
  • 您也可以通过if ((val_input &gt;&gt; (sizeof val_input * CHAR_BIT - 1)) &amp; 1)检查是否设置了符号位(单精度浮点为第31位,双精度浮点为int或第63位点和 8 字节整数值)CHAR_BIT 是每个字符的位数(通常为 8),在limits.h 中定义。
  • 如果一行输入超过1个数字会怎样?
  • 1.234 这样的不是整数的数字输入会发生什么?

标签: c loops


【解决方案1】:

首先,don't use scanf。如果stdin 与预期的不匹配,它将把它留在缓冲区中并继续重新读取相同的错误输入。调试起来很郁闷。

const int max_values = 10;

for (int i = 0; i <= max_values; i++) {
    int value;
    if( scanf("%d", &value) == 1 ) {
        printf("Got %d\n", value);
    }
    else {
        fprintf(stderr, "I don't recognize that as a number.\n");
    }
}

观察当你给它喂一些不是数字的东西时会发生什么。它只是不断尝试一遍又一遍地阅读坏行。

$ ./test
1
Got 1
2
Got 2
3
Got 3
foo
I don't recognize that as a number.
I don't recognize that as a number.
I don't recognize that as a number.
I don't recognize that as a number.
I don't recognize that as a number.
I don't recognize that as a number.
I don't recognize that as a number.
I don't recognize that as a number.

相反,使用fgets 可靠地读取整行并使用sscanf 解析它。 %f 用于浮点数、十进制数。使用%d 仅识别整数。然后检查是否为阳性。

#include <stdio.h>

int main() {
    const size_t max_values = 10;
    int values[max_values];
    char buf[1024];

    size_t i = 0;
    while(
        // Keep reading until we have enough values.
        (i < max_values) &&
        // Read the line, but stop if there's no more input.
        (fgets(buf, sizeof(buf), stdin) != NULL)
    ) {
        int value;

        // Parse the line as an integer.
        // If it doesn't parse, tell the user and skip to the next line.
        if( sscanf(buf, "%d", &value) != 1 ) {
            fprintf(stderr, "I don't recognize that as a number.\n");
            continue;
        }

        // Check if it's a positive integer.
        // If it isn't, tell the user and skip to the next line.
        if( value < 0 ) {
            fprintf(stderr, "Only positive integers, please.\n");
            continue;
        }

        // We got this far, it must be a positive integer!
        // Assign it and increment our position in the array.
        values[i] = value;
        i++;
    }

    // Print the array.
    for( i = 0; i < max_values; i++ ) {
        printf("%d\n", values[i]);
    }
}

请注意,由于用户可能输入错误的值,我们不能使用简单的 for 循环。相反,我们循环直到我们读取了足够的 有效 值,或者没有更多的输入。

【讨论】:

  • @Acorn:如果您不处理交互式输入,则不需要提示。
  • @Acorn We get so many questions about scanf 归结为“扫描缓冲区中的剩余内容”和“检查返回值”。很多。这么多。
  • @Acorn 这表明人们被scanf 的陷阱抓住的频率。
  • @Acorn -- “不要使用scanf()”是非常好的建议,尤其是对于学习者。这个功能很难正确使用,而且有很多黑暗的角落,即使是那些知道自己在做什么的人也会被它的怪癖抓住。几乎总有比scanf() 更好的解决方案。
  • @Acorn -- 它更简单、更灵活。使用fgets() 后跟sscanf() 意味着输入的行被提取并存储在一个很大的缓冲区中,当输入与初始期望不匹配时可以再次扫描该缓冲区。重新扫描输入的能力使其更加灵活。除非输入太大,否则在调用fgets()stdin 将是空的(因此缓冲区很大),这意味着不需要清理输入流的代码。
【解决方案2】:

像这样简单的事情可能对你有用:

int n;
int ret;

for (;;) {
    ret = scanf("%d", &n);

    if (ret == EOF)
        break;

    if (ret != 1) {
        puts("Not an integer");
        for (;;)
            if (getchar() == '\n')
                break;
        continue;
    }

    if (n < 0) {
        puts("Not a positive integer");
        continue;
    }

    printf("Correct value %d\n", n);

    /* Do your min/max/avg calculation */
}

/* Print your results here */

这只是一个示例,假设您不需要读取浮点数然后检查它们是否为整数,以及其他一些东西。但对于初学者来说,它很简单,您可以在此基础上工作。

要跳出循环,您需要传递EOF(通常在 Linux/macOS 终端中为 Ctrl+D,在 Windows 终端中为 Ctrl+Z)。

【讨论】:

    【解决方案3】:

    简单便携的解决方案

    #include <limits.h>
    #include <stdio.h>
    int get_positive_number() {
      char buff[1024];
      int value, ch;
      while (1) {
        printf("Enter positive number: ");
        if (fgets(buff, 1023, stdin) == NULL) {
          printf("Incorrect Input\n");
          // Portable way to empty input buffer
          while ((ch = getchar()) != '\n' && ch != EOF)
            ;
          continue;
        }
        if (sscanf(buff, "%d", &value) != 1 || value < 0) {
          printf("Please enter a valid input\n");
        } else {
          break;
        }
      }
      return value;
    }
    void solution() {
      // Handling malformed input
      // Memory Efficient (without using array to store values)
      int n;
      int min = INT_MAX;
      int max = INT_MIN;
      double avg = 0;
      printf("Enter number of elements: ");
      scanf("%d", &n);
      getc(stdin);
      int value;
      for (int i = 0; i < n; i++) {
        value = get_positive_number();
        if (value > 0) {
          if (min > value) {
            min = value;
          }
          if (max < value) {
            max = value;
          }
          avg += value;
        }
      }
      avg = avg / n;
      printf("Min = %d\nMax = %d\nAverage = %lf\n", min, max, avg);
    }
    int main() {
      solution();
      return 0;
    }
    
    • 输出:


    Enter number of elements: 3
    Enter positive number: 1
    Enter positive number: 2
    Enter positive number: a
    Please enter a valid input
    Enter positive number: -1
    Please enter a valid input
    Enter positive number: 1
    Min = 1
    Max = 2
    Average = 1.333333
    

    【讨论】:

    • 不是我的反对意见,但是……fseek(stdin, 0, SEEK_END); 行是值得商榷的。很多时候,stdin 不是可搜索设备。由于一行错误而跳过整个输入文件可能过于严厉。 (另请参阅 Using fflush(stdin) 了解有关此主题的更多信息。)我还希望看到 printf("%d\n", n); 在格式字符串末尾带有换行符。
    • 这不会打印任何错误消息,也不会读取值进行处理,只是搜索第一个正确的。
    • 你最好使用fgetssscanf 然后尝试修补scanf
    • 请注意,如果用户仅指示 EOF(n 未初始化使用),或者如果用户键入一个字母(或其他非数字字符)而不是数字。您必须检查来自scanf() 的返回值。如果scanf() 返回EOF,你应该放弃循环;如果它返回0,您可能应该考虑读取字符直到下一个换行符,或者可能直到下一个可能是数字的字符——+- 或读取整数时的数字。强大的输入令人沮丧。
    • 当输入格式错误时,此代码具有未定义的行为,例如,如果用户输入字母而不是数字。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多