【问题标题】:Problem with fgets() and sscanf() in an calculator exercise计算器练习中 fgets() 和 sscanf() 的问题
【发布时间】:2021-03-05 17:19:33
【问题描述】:

我正在努力完成一项我必须做的运动。因此我必须编写一个计算器。它只需要知道 int 和 +、-、/ 和 * 等运算符,我们需要使用 fgets() 和 sscanf()。

例如,如果我首先输入 5+6,它完全可以正常工作。作为输出,我得到了正确的输出:5+6=11。如果我的第二个条目类似于“a”或“4”,它不会像它应该那样打印出“无效输入”。相反,它会打印 4+6=10,因此加号和 6 会自动相加。我认为它与 fgets() 及其内存有关,但我不知道如何解决这个问题。 到目前为止,这是我的代码:

#include <stdio.h>

int main(int argc, char *argv[])
{

   /* First define the variables, which are needed. 
There are two numbers, type int, and one op for the operation. */
   int number1, number2;
   char operator;
   char entry[50];

   /* First Output, where numbers and Operation are added, after that the code has to scan, which numbers and which operator were choosen.*/

   while (1)
   {
      printf("Enter <int> <op> <int>, single '0' to exit:");
      
      fgets(entry, sizeof(entry), stdin);
      sscanf(entry, "%d %c %d", &number1, &operator, & number2);
   
      /* Different calculations regarding the operator*/

      if (operator== '+')
      {
         printf("%d + %d = %d", number1, number2, number1 + number2);
      }

      else if (operator== '-')
      {
         printf("%d - %d = %d", number1, number2, number1 - number2);
      }

      else if (operator== '*')
      {
         printf("%d * %d = %d", number1, number2, number1 * number2);
      }

      else if (operator== '/')
      {
         printf("%d / %d = %d", number1, number2, number1 / number2);
      }
      else if (number1 == 0)
      {
         printf("Goodbye");
         break;
      }
      else if (number2=="\0" && operator =="\0" ||operator=="\0" )
      {
         printf("invalid input");
      }
      else
      {
         printf("invalid input");
      }
      printf("\n");
   }
   return 0;
}

感谢您的帮助:)

例子:

Enter ,单 '0' 退出:4+5 4 + 5 = 9

Enter ,单 '0' 退出:f 4 + 5 = 9

Enter ,单 '0' 退出:g 4 + 5 = 9

Enter ,单 '0' 退出:6-4 6 - 4 = 2

Enter ,单 '0' 退出:7 7 - 4 = 3

Enter ,单 '0' 退出:gergevyd 7 - 4 = 3

【问题讨论】:

  • 检查sscanf的返回值并处理错误情况。目前,您忽略它并使用无效的输入值继续耕作。
  • 谢谢 :) 我尝试这样做,所以我添加了更多其他 if 语句,但这并没有什么机会。好像是覆盖什么的
  • 我试过这样做。你需要展示你尝试过的东西。除非您展示出来,否则我们无法判断您是否正确操作。
  • @Saphira14 如果fgets 返回NULLsscanf 不返回3,那么您没有有效的输入,因此没有指向if/else 的值。跨度>
  • 您说您尝试过,但实际上并没有按照您的建议“检查 sscanf 的返回值”。

标签: c scanf calculator fgets


【解决方案1】:

您遇到的问题是,正如@kaylum 指出的那样,您没有验证转换结果。代码中的验证与实现正确的逻辑同样重要(如果由于验证失败而导致未定义行为更是如此)

您必须验证每个用户输入和每个转换。通过fgets() 接受输入,您正在做正确的事情。使用面向行的 函数进行用户输入可以避免大量问题,因为您一次会消耗一整行输入。如果稍后转换失败,则可以保护您免受stdin 未读中剩余的任何错误字符的影响。 (您可以检查输入的strlen()'\n' 存储在entry 缓冲区中的内容的末尾以进行进一步验证)

但是,您仍然需要检查 fgets() 的返回,因为用户通过按 Ctrl + D(或 Ctrl + z on windows)是一个有效的输入,可以生成手动EOF。所以只需检查来自fgets() 的返回是否为NULL,这将指示EOF,例如

    ...
    if (!fgets(entry, sizeof(entry), stdin)) {      /* validate EVERY input */
        puts ("(user canceled input)");             /* handle manual EOF case */
        break;
    }

使用sscanf(),您必须检查每个有效转换是否成功,并且您没有遇到匹配失败(如输入'a' 为整数)或输入-failure 在第一次有效转换之前达到EOFsscanf() 返回发生的成功转换次数,因此只需计算您的 conversion-specifiers,然后确保返回等于该数字,例如

    /* validate EVERY conversion */
    if (sscanf (entry, "%d %c %d", &number1, &operator, & number2) != 3) {
        fputs ("error: invalid integer input.\n", stderr);
        continue;                                   /* go get new input */
    }
    ...

注意:在失败时使用continue;,您只需让程序丢弃当前行并提示用户输入新行)


其他想法

关于您的登录,这是我的一些进一步的想法(包括下面的内联 cmets)。当您需要一个常量时,#define 一个(或多个)。这样,您可以在代码顶部有一个方便的位置,以便在需要时进行更改,并且您不必通过声明或循环限制进行挑选。当您使用sizeof 时,正确的语法是sizeof objectsizeof (type)。仅当您请求type 的大小时才需要括号(但在其他情况下不会出错)

当您使用单个数字或字符来确定要采用多个分支中的哪一个时,请考虑使用switch() 语句而不是if .. else if ... else if ... else ... 语句链。方便多了。

在进行除法时——始终防止"divide by zero"(否则会发生坏事——尽管生成的代码通常会将它们作为异常处理)。

使用fgets() 时,您可以提供一种非常简单的方法来指示输入结束。由于fgets()(以及POSIX getline())读取并存储'\n'作为它们填充的缓冲区的一部分,您只需检查entry中的第一个字符是否为'\n'即可退出(它是方便的方式去)。在您拨打fgets() 之后,您只需检查*entry == '\n' 是否确定用户是否已完成。 (*entry == *(entry + 0) == entry[0]) 指针的简单取消引用是检查第一个字符(或第一个元素)的快速方法。

考虑到这些,您可以考虑如下:

#include <stdio.h>

#define MAXC 1024       /* if you need a constant, #define one (or more) */

int main (void) {       /* if no arguments are expected */

    int number1, number2;
    char entry[MAXC], operator;
    
    while (1)   /* loop continually */
    {   /* no special input to exit, break read-loop on blank line */
        fputs ("\n('Enter' to exit)\nEnter expression: int op int: ", stdout);
        
        /* while not EOF or blank line */
        if (!fgets (entry, sizeof entry, stdin) || *entry == '\n') {
            puts ("(user canceled input)");
            break;
        }
        
        /* validate EVERY conversion */
        if (sscanf (entry, "%d %c %d", &number1, &operator, & number2) != 3) {
            fputs ("  error: invalid integer input.\n", stderr);
            continue;                                   /* go get new input */
        }
        
        /* Different calculations regarding the operator*/
        switch (operator)
        {               /* just add \n to end of each format string */
            case '+':   printf ("%d + %d = %d\n", number1, number2, number1 + number2);
                        break;
            case '-':   printf ("%d - %d = %d\n", number1, number2, number1 - number2);
                        break;
            case '*':   printf ("%d * %d = %d\n", number1, number2, number1 * number2);
                        break;
            case '/':
                        if (number2 == 0)   /* handle divide by zero */
                            fputs ("  error: division by 0 exception.\n", stderr);
                        else
                            printf ("%d / %d = %d\n", number1, number2, number1 / number2);
                        break;
            default:    fprintf (stderr, "  error: invalid operator '%c'.\n", operator);
                        break;
        }
    }
}

(注意:增加entry 可以容纳的字符数。不要吝啬缓冲区大小。除非你在微控制器上,一个 1k 的缓冲区很好,可以处理除了最坏的情况之外的所有情况 steps-on-the-keyboard)

使用/输出示例

$ ./bin/calculator

('Enter' to exit)
Enter expression: int op int: 1 + 1
1 + 1 = 2

('Enter' to exit)
Enter expression: int op int: 1 + z
  error: invalid integer input.

('Enter' to exit)
Enter expression: int op int: 4 | 8
  error: invalid operator '|'.

('Enter' to exit)
Enter expression: int op int: 10/1
10 / 1 = 10

('Enter' to exit)
Enter expression: int op int: 10/0
  error: division by 0 exception.

('Enter' to exit)
Enter expression: int op int: 25 - 13
25 - 13 = 12

('Enter' to exit)
Enter expression: int op int:
(user canceled input)

检查一下,如果您有任何问题,请告诉我。

【讨论】:

  • 好的,感谢您的帮助!我现在正在尝试您的解决方案......对不起,我只是一个初学者,我之前只使用过 python,这完全不同
  • 别担心,我很乐意为您提供进一步的帮助,如果您遇到问题,请在这里告诉我。
  • 感谢您的帮助 :) 现在工作正常。我现在将尝试捕捉每个用户的输入,看看我是否忘记了。如果有任何问题,我会问你,谢谢
  • @Saphira14 尝试:fputs ("\n('Enter' to exit)\nEnter expression: int op int: ", stdout); 用于提示,然后if (!fgets (entry, sizeof entry, stdin) || *entry == '\n') {... 用于阅读。也可以使用switch() 来检查运算符。
  • 还要注意你正在做 integer 除法所以10 / 6 = 0。您可以使用printf ("%d / %d = %.2lf\n", number1, number2, (double)number1 / number2); 解决此问题
猜你喜欢
  • 2017-08-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多