| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 3602 人关注过本帖
标题:分享:中缀表达式转后缀表达式
只看楼主 加入收藏
Emotiona
Rank: 7Rank: 7Rank: 7
等 级:黑侠
帖 子:311
专家分:581
注 册:2017-3-7
结帖率:97.5%
收藏
已结贴  问题点数:20 回复次数:19 
分享:中缀表达式转后缀表达式
写的一个简单的计算器,能简单+ - * / ^ 运算,没有考虑计算结果溢出问题。
处理了负号运算,合法的负号。只能是第一个数为负数,左括号开头的第一个数为负数,以及括号括起来的负数。
希望各位大牛指点下哪里不足。
程序代码:
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <math.h>

#define MAXSIZE 10000

struct Stackop
{
    char op[MAXSIZE];
    int top;
}Stackop;

struct Stacknum
{
    double num[MAXSIZE];
    int top;
}Stacknum;

int GetLevel(char op)            //得到运算符优先级
{
    if (op == '+' || op == '-')
        return 1;
    else if (op == '*' || op == '/')
        return 2;
    else if (op == '^')
        return 3;
    else
        return -1;
}

double Caclulate(double a, double b, char symbol, int *flag)
{
    switch (symbol)                        //数据运算,以及除数不能为0
    {
    case '+' :
        return a + b;
    case '-' :
        return a - b;
    case '*' :
        return a * b;
    case '/':
        if (b == 0)
            *flag = 0;
        else
            return  a / b;
        break;
    case '^' :
        return pow(a, b);
    default :
        *flag = 0;
        break;
    }
    return 0;
}

int GetRpn(char *calc, char *rpn)            //中缀表达式转后缀表达式
{
    int len, i, flag, cnt, error;

    error = flag = cnt = 0;
    Stackop.top = -1;                        //字符栈为空
    len = strlen(calc);
    if (calc[len - 1] == '=')                //考虑最后是否为=
        calc[len - 1] = '\0';
    for (i = 0; calc[i] != '\0'; i++)        //将负数和-号区别,负数用!代替
        if (calc[0] == '-' || calc[i] == '-' && calc[i - 1] == '(')
            calc[i] = '!';
    for (i = 0; calc[i] != '\0'; i++)            
    {
        if (isdigit(calc[i]) || calc[i] == '!')            //数字处理过程,如果扫描到的是数字,直接加入到后缀数组中
        {
            if (!isdigit(calc[i]))
            {
                i++;
                flag = 1;
            }
            if (!isdigit(calc[i]))
                return 0;
            
            while (isdigit(calc[i]))
            {
                rpn[cnt++] = calc[i];
                i++;
            }
            if (calc[i] == '.') {
                rpn[cnt++] = calc[i];
                ++i;
                while (isdigit(calc[i]))
                {
                    rpn[cnt++] = calc[i];
                    i++;
                }
            }
            if (flag)                
            {
                rpn[cnt++] = '#';        //如果是负数末尾加#加!,表示一个负数结束
                rpn[cnt++] = '!';
                flag = 0;
            }
            else
            { 
                rpn[cnt++] = '#';        //当是整数末尾加#,表示一个正数结束
            }
        }
        
        if (calc[i] == '\0')
            break;
        if (calc[i] == '(')                //如果是左括号直接进符号栈
            Stackop.op[++Stackop.top] = calc[i];
        else if (calc[i] == ')') {                    //当匹配到右括号出栈
            while (Stackop.op[Stackop.top] != '(')        //如果栈顶是左括号,直接结束,相当于空括号
            {
                rpn[cnt++] = Stackop.op[Stackop.top];    //挨个把括号里面的运算符转换到后缀数组中
                --Stackop.top;
            }                
            --Stackop.top;                        //把匹配到的左括号出栈
        }
        else if (Stackop.op[Stackop.top] != -1 &&  GetLevel(calc[i]) <= GetLevel(Stackop.op[Stackop.top]))
        {                                        //扫描到的运算符如果比栈顶优先级低,栈里所有优先级低于扫描到运算符优先级的运算符
                                                //加入到后缀数组
            while (Stackop.op[Stackop.top] != -1 && (error = GetLevel(calc[i])) <= GetLevel(Stackop.op[Stackop.top]))
            {
                if (error == -1)
                    return 0;
                rpn[cnt++] = Stackop.op[Stackop.top];
                Stackop.top--;
            }
            Stackop.op[++Stackop.top] = calc[i];    //如果符号栈为空,或者栈里优先级大于扫描到的字符优先级,把扫描到的运算符加入字符栈 
        }
        else
            Stackop.op[++Stackop.top] = calc[i];    //字符栈空或者优先级比扫描到的运算符优先级大, 假如到字符栈
        
    }
    while (Stackop.top != -1) {
        rpn[cnt++] = Stackop.op[Stackop.top];    //到最后字符栈还有运算符,一次加入到后缀数组,最后得到后缀表达式
        Stackop.top--;
    }
    rpn[cnt] = '\0';
    return 1;
}

int GetAns(char *rpn, double *result)
{
    int i, flag = 1;
    double num, cnt;
    double a, b, val;

    Stacknum.top = -1;                    //这里开始就是后缀表达式计算过程
    for (i = 0; rpn[i] != '\0'; i++)    //到这里就是入栈和出栈的过程,只要是数字就直接入栈;是字符,数字栈出栈两个元素进行运算后入栈
    {                                    //反复此过程,到最后栈顶元素就只有一个,就是我们要的计算结果

        if (rpn[i] >= '0' && rpn[i] <= '9') {
            num =  0;
            cnt = 1.0;
            while (rpn[i] != '#' && rpn[i] != '.')
            {
                num = num * 10.0 + (rpn[i] - '0');
                i++;
            }
            if (rpn[i] == '.')
            {
                i++;
                    while (rpn[i] != '#')
                    {
                        num = num * 10.0 + (rpn[i] - '0');
                        cnt *= 10;
                        i++;
                    }
            }
            if (rpn[i + 1] == '!')
            {
                i++;
                Stacknum.num[++Stacknum.top] = num / cnt * (-1);    //字符串转换为数字或者浮点数入数字栈
            }
            else
                Stacknum.num[++Stacknum.top] = num / cnt;
        }
        else
        {                        //匹配到运算符,两个数字出栈参加运算再进栈
            b = Stacknum.num[Stacknum.top--];
            if (Stacknum.top == -1)
                return 0;
            a = Stacknum.num[Stacknum.top--];
            val = Caclulate(a, b, rpn[i], &flag);
            Stacknum.num[++Stacknum.top] = val;

        }
    }
    *result = Stacknum.num[Stacknum.top];
    if(!flag)
    return 0;
    return 1;
}

int main()
{
    char calc[MAXSIZE], rpn[MAXSIZE];
    double result;

    while (1)
    {
        printf("Please input calculate expression and expression can't hava special symbols!\n\n");
        scanf("%[^\n]%*c", calc);    //表示读一行,解决scanf不能读空格的问题
        if (!GetRpn(calc, rpn)) 
        {
            printf("\nError: expresion error!\n");
            continue;
        }
        if (GetAns(rpn, &result))
            printf("\nCalculate val = %lf\n\n", result);
        else
            printf("\nError: expresion error!\n");

    }
    return 0;
}


[此贴子已经被作者于2017-5-28 09:42编辑过]

搜索更多相关主题的帖子: 计算器 表达式 
2017-05-27 14:46
九转星河
Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19
来 自:长长久久
等 级:贵宾
威 望:52
帖 子:5023
专家分:14003
注 册:2016-10-22
收藏
得分:10 
renkejun1942曾经写过类似的~可以找他的来对比一下~

[code]/*~个性签名:bug是什么意思?bug是看上去没有可能的东西实际上是有可能做到的 就是这样~2018-08-08更~*/[/code]
2017-05-27 15:44
renkejun1942
Rank: 14Rank: 14Rank: 14Rank: 14
来 自:不是这样
等 级:贵宾
威 望:33
帖 子:1645
专家分:5297
注 册:2016-12-1
收藏
得分:10 
结果是……没有结果。

图片附件: 游客没有浏览图片的权限,请 登录注册


这个表达式的错误在哪儿?
图片附件: 游客没有浏览图片的权限,请 登录注册


再来一发死循环。
图片附件: 游客没有浏览图片的权限,请 登录注册



PS:你的那个管理系统的时候,我就提醒过你,函数太长,我挺好奇,你现在的代码,该如何Debug。

[此贴子已经被作者于2017-5-27 19:22编辑过]


09:30 05/21 种下琵琶种子,能种活么?等待中……
21:50 05/27 没有发芽。
20:51 05/28 没有发芽。
23:03 05/29 没有发芽。
23:30 06/09 我有预感,要发芽了。
2017-05-27 19:15
Emotiona
Rank: 7Rank: 7Rank: 7
等 级:黑侠
帖 子:311
专家分:581
注 册:2017-3-7
收藏
得分:0 
提示不能输入特殊字符,空格没处理,我直接是scanf输入的,死循环也正常的。5--5数学上也不能这样表示吧
图片附件: 游客没有浏览图片的权限,请 登录注册

图片附件: 游客没有浏览图片的权限,请 登录注册
2017-05-27 19:29
renkejun1942
Rank: 14Rank: 14Rank: 14Rank: 14
来 自:不是这样
等 级:贵宾
威 望:33
帖 子:1645
专家分:5297
注 册:2016-12-1
收藏
得分:0 
数学上不能这样表示?是这样子吗?


[此贴子已经被作者于2017-5-27 19:34编辑过]


09:30 05/21 种下琵琶种子,能种活么?等待中……
21:50 05/27 没有发芽。
20:51 05/28 没有发芽。
23:03 05/29 没有发芽。
23:30 06/09 我有预感,要发芽了。
2017-05-27 19:32
Emotiona
Rank: 7Rank: 7Rank: 7
等 级:黑侠
帖 子:311
专家分:581
注 册:2017-3-7
收藏
得分:0 
回复 5楼 renkejun1942
大哥,数学上不能这么表示的。1----1你说是不是非法的表达式。还有那个是循环你是怎么输入的
2017-05-27 19:35
renkejun1942
Rank: 14Rank: 14Rank: 14Rank: 14
来 自:不是这样
等 级:贵宾
威 望:33
帖 子:1645
专家分:5297
注 册:2016-12-1
收藏
得分:0 
回复 6楼 Emotiona
1----1这样写当然是非法的,但是1--5确实合法的,也许我数学老师死的太早,讲到负数加减法的时候,从来没告诉过我们这样写还得加一个括号。

09:30 05/21 种下琵琶种子,能种活么?等待中……
21:50 05/27 没有发芽。
20:51 05/28 没有发芽。
23:03 05/29 没有发芽。
23:30 06/09 我有预感,要发芽了。
2017-05-27 19:37
Emotiona
Rank: 7Rank: 7Rank: 7
等 级:黑侠
帖 子:311
专家分:581
注 册:2017-3-7
收藏
得分:0 
回复 7楼 renkejun1942
处理了负号运算,合法的负号。只能是第一个数为负数,左括号开头的第一个数为负数,以及括号括起来的负数。
我记得数学上的负数是这样子的。
2017-05-27 19:40
renkejun1942
Rank: 14Rank: 14Rank: 14Rank: 14
来 自:不是这样
等 级:贵宾
威 望:33
帖 子:1645
专家分:5297
注 册:2016-12-1
收藏
得分:0 
让你的程序死循环很容易,快速输入字母,然后回车,多来几次就死循环了。

让你的程序崩溃更容易,连续两个回车就崩溃了。

输入Ctrl+Z也会崩溃,方法多的是。

09:30 05/21 种下琵琶种子,能种活么?等待中……
21:50 05/27 没有发芽。
20:51 05/28 没有发芽。
23:03 05/29 没有发芽。
23:30 06/09 我有预感,要发芽了。
2017-05-27 19:42
Emotiona
Rank: 7Rank: 7Rank: 7
等 级:黑侠
帖 子:311
专家分:581
注 册:2017-3-7
收藏
得分:0 
回复 9楼 renkejun1942
好吧,确实多。没有去处理这些。后面再改进
2017-05-27 19:45
快速回复:分享:中缀表达式转后缀表达式
数据加载中...
 
   



关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.021349 second(s), 9 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved