c - 计算器练习中 fgets() 和 sscanf() 的问题
问题描述
我正在努力做我必须做的运动。因此我必须编写一个计算器。它只需要知道 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;
}
谢谢你的帮助 :)
例子:
输入,单 '0' 退出:4+5 4 + 5 = 9
输入,单 '0' 退出:f 4 + 5 = 9
输入,单 '0' 退出:g 4 + 5 = 9
输入,单 '0' 退出:6-4 6 - 4 = 2
Enter , 单 '0' 退出:7 7 - 4 = 3
输入,单 '0' 退出:gergevyd 7 - 4 = 3
解决方案
正如@kaylum 指出的那样,您遇到的问题是,您没有验证转换结果。代码中的验证与实现正确的逻辑一样重要(如果由于验证失败而导致未定义的行为,则更是如此)
您必须验证每个用户输入和每个转换。您正在做正确的事情来接受输入fgets()
。使用面向行的函数进行用户输入可以避免大量问题,因为您一次会使用整行输入。如果稍后转换失败,那么您将受到保护,不会出现任何stdin
未读的错误字符。(您可以检查strlen()
输入的 和'\n'
存储在缓冲区中的内容的末尾以entry
进行进一步验证)
但是,您仍然需要检查返回,因为用户通过按下(或在 Windows 上)fgets()
取消输入是生成手动的有效输入。因此,只需检查 return from是否会表明,例如Ctrl + DCtrl + zEOF
fgets()
NULL
EOF
...
if (!fgets(entry, sizeof(entry), stdin)) { /* validate EVERY input */
puts ("(user canceled input)"); /* handle manual EOF case */
break;
}
使用sscanf()
,您必须检查每个有效转换是否成功,并且您没有遇到匹配失败(如输入'a'
整数)或在第一次有效转换之前到达的输入失败。返回发生的成功转换次数,因此只需计算您的转换说明符,然后确保返回等于该数字,例如EOF
sscanf()
/* 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;
失败,您只会使程序丢弃当前行并提示用户输入新行)
额外的想法
关于您的登录,这是我的一些进一步想法(包括在下面的评论中)。当你需要一个常数时,#define
一个(或多个)。这样,您可以在代码顶部有一个方便的位置,以便在需要时进行更改,并且您不必通过声明或循环限制进行挑选。使用sizeof
时,正确的语法是sizeof object
or sizeof (type)
。仅当您请求 a 的大小时才需要括号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 缓冲区很好,可以处理除猫踩踏的最坏情况外的所有情况-键盘)
示例使用/输出
$ ./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)
看看事情,如果你有问题,请告诉我。
推荐阅读
- rabbitmq - RabbitMQ - Windows 上的 erl.exe 高 CPU 消耗
- c# - 启动进程以打开临时 PDF 文件并在完成后删除文件
- c - Bazel:使用宏从列表中生成构建规则
- javascript - 使用 jQuery 将 jQuery 悬停应用到多个元素
- javascript - 在 react 中使用 array.map 动态创建组件时未渲染的组件
- google-app-maker - Dynamically create widgets based on records in a datasource
- c# - 从类库项目托管 ASP.NET CORE 2
- orientdb - OrientDB 不一致的行为调用 reload()
- html - chrome 对于“item”值的行为不同
- ruby-on-rails - 如何在 ruby on rails 中运行迁移后更新数据库模式