C 中的计算器应该只接受 float/int 个数字

Calculator in C should only accept float/int numbers

我正在开发一个只有基本运算符的小型计算器。它工作得很好,就像我想要的那样。但是有一个小问题。 我的程序是在一个循环中,因此用户理论上可以在每次计算后再次使用它。 但我也希望程序能够区分 float 类型的数字和所有其他元素,因此它只接受浮点数或整数。

问题来了: 如果我输入字母字符,问题就会变得混乱并且无法正确循环。 您可以在输入一些随机字母而不是预期的两个数字时自己尝试一下。

#include <stdio.h>
#include <unistd.h>
#include <ctype.h>

int main()
{
    float num1, num2, result = 0;
    int menu;

    while (1)       //so that the program basically never stops
    {
        printf("--- Taschenrechner ---\n\n"
               "1. Addition\n"
               "2. Subtraktion\n"
               "3. Multiplikation\n"
               "4. Division\n"
               "5. Beenden\n"
               "Wählen Sie Ihren gewünschten Operator aus (oder auch nicht): ");
        // it is like the calculators menu, distinguishing between
        // addition, subtraction, multiplication, division and exit
        scanf("%d", &menu);
        if (menu >= 5 || menu < 1)
        {
            printf("\nDas Programm wurde beendet, schade. Bis zum nächsten Mal!");      //if the given integer is 5 or not part of the menu, the program should stop
            break;
        }
        printf("\n\nGeben Sie nun zwei Zahlen ein:\n");     //user should provide two numbers
        scanf("%f", &num1);
        printf("\n");
        scanf("%f", &num2);
        if ((isalpha(num1) || isalpha(num2)) == 0)      //if the given elements are no numbers at all, in this case part of the alphabet, the program should stop
        {
            printf("Gut!");
        } else
        {
            printf("Break");
            break;
        }
        switch (menu)
        {
        case 1:
            result = num1 + num2;
            break;
        case 2:
            result = num1 - num2;
            break;
        case 3:
            result = num1 * num2;
            break;
        case 4:
            result = num1 / num2;
            break;
        default:
            printf("\n\nUps, da ist wohl etwas mit den Operatoren schief gelaufen. Versuchen Sie es erneut!");      //here again, if something went wrong within the switch case, program should stop
            break;
        }
        printf("\n\nPerfekt, das hat geklappt!");
        sleep(1);       //this is just for delaying the result 
        printf("\n\nIhr Ergebnis wird berechnet\nErgebnis: %.2f\n\n\n", result);
        sleep(3);
    }
    return 0;
} 

我真的不知道如何解决这个问题,试了好几天了。解决方案可能真的很简单,但我就是不明白。 一点帮助会非常好。 :)

也不介意节目是德语的。我已经解释了一些东西作为评论。

您应该检查 scanf 编辑的值 return。例如:if( scanf("%d", &menu) == 1 ) { ... }。如果输入流的下一个字符不是有效整数的一部分,上面的 scanf 将 return 0.

一般来说,您应该始终检查由 scanf return编辑的值。

为确保用户键入有效输入,您应该验证 scanf() 操作是否成功,并在数据无效时要求用户重新输入。 scanf() returns 成功转换的次数。如果输入无效,您应该阅读并丢弃当前输入行的其余部分。

还要注意 if ((isalpha(num1) || isalpha(num2)) == 0) 是不正确的:它测试 num1num2 是否是 ASCII 字母的代码。如果使用类型 6597...

,这将错误地检测到无效输入

这是修改后的版本:

#include <stdio.h>
#include <unistd.h>

int main() {
    float num1, num2, result = 0;
    int menu;

    while (1) {       //so that the program basically never stops
        printf("--- Taschenrechner ---\n\n"
               "1. Addition\n"
               "2. Subtraktion\n"
               "3. Multiplikation\n"
               "4. Division\n"
               "5. Beenden\n"
               "Wählen Sie Ihren gewünschten Operator aus (oder auch nicht): ");
        // it is like the calculators menu, distinguishing between
        // addition, subtraction, multiplication, division and exit

        if (scanf("%d", &menu) != 1 ||  // invalid menu entry, bail out
            menu >= 5 || menu < 1) {
            printf("\nDas Programm wurde beendet, schade. Bis zum nächsten Mal!");      //if the given integer is 5 or not part of the menu, the program should stop
            break;
        }
        for (;;) {
            printf("\n\nGeben Sie nun zwei Zahlen ein:\n");     //user should provide two numbers
            if (scanf("%f%f", &num1, &num2) == 2)
                break;
            printf("\nUngültige Eingabe\n");
            if (scanf("%*[^\n]") == EOF)    // discard the rest of the input line
                return 1;
        }
        switch (menu) {
          case 1:
            result = num1 + num2;
            break;
          case 2:
            result = num1 - num2;
            break;
          case 3:
            result = num1 * num2;
            break;
          case 4:
            result = num1 / num2;
            break;
          default:
            printf("\n\nUps, da ist wohl etwas mit den Operatoren schief gelaufen. Versuchen Sie es erneut!\n");
            //here again, if something went wrong within the switch case, program should stop
            return 1;
        }
        printf("\n\nPerfekt, das hat geklappt!");
        sleep(1);       //this is just for delaying the result 
        printf("\n\nIhr Ergebnis wird berechnet\nErgebnis: %.2f\n\n\n", result);
        sleep(3);
    }
    return 0;
} 

备注:

  • scanf() 无法转换 2 个浮点数的原因有 2 个:

    • 文件过早结束,
    • 流中的违规字符不能作为数字的开头,例如 A%
  • 要刷新用户输入的行的剩余部分,可以使用循环:

    int c;
    while ((c = getchar()) != EOF && c != '\n')
        continue;
    if (c == EOF) {
        /* no more characters available from stdin: bail out */
        return 1;
    }
    

或者可以使用一种 scanf() 转换格式,该格式将读取并丢弃直到换行符的所有字符:

  if (scanf("%*[^\n]") == EOF)    // discard the rest of the input line
      return 1;

解释如下:

  • % 之后的 * 阻止存储转换后的值,转换说明符不需要指针。
  • [...] 指定一个 scanset,一组要接受或拒绝的字符。 [abcx-z] 接受 abcxzx 之间的任何字符的任何非空组合和 z(ASCII 字符集中的 y)。 [^\n] 接受不同于 \n 的任何非空字符序列,因此输入行的其余部分(如果有的话)。
  • 用户键入的无法与行的其余部分一起转换的字符将被读取并丢弃,留下换行符。
  • 如果换行前没有这样的字符可用,scanf() returns 0 并且换行仍在等待下一次%f转换;
  • 如果流立即到达文件末尾,scanf() returns EOF 将导致程序退出。