表达式计算

来源:互联网 发布:重庆北碚网络花店 编辑:程序博客网 时间:2024/05/16 15:52
表达式计算笔记
三个步骤:读入,分离数据与符号,计算。

读入

字符串输入可以用scanf()和gets()完成。scanf("%s")遇到空格就中断,而输入中可能含有空格,因此放弃scanf()。gets()只有在遇到EOF和换行符的时候,才会停止读入,并不进行边界检查,所以当字符数组大小小于输入的字符串时,会导致溢出。同样有这个问题的还有strcpy(),strcat(),sprintf(),scanf(),sscanf(),fscanf(),vfscanf(),vsprintf(),vscanf(),vsscanf(),streadd(),strecpy(),strtrns()等。改进方法是使用fgets()替代gets()。

char *fgets(char *buf,              //用来存储所得数据的地址的指针int bufsize,             //读入数据的大小FILE *stream)            //将要读取的文件流的指针

fgets()是一个从文件里读入的函数,由于这里是直接输入,所以第三个参数是stdin(这是stdio.h里规定的),第二个参数直接用开出的字符数组长度,第一个就是数组头指针了。


分离数据

stdlib.h里有一系列类型转换的函数,strtod,strtof,strtod,strtol,strtoll,strtoul,strtoull等。这里可以使用的是strtod,将字符串转化为double型数据。

double strtod(const char *nptr,      //字符串地址char **endptr);         //读完后指向剩余的第一个字符
函数会跳过nptr所指向的字符串中的空白字符,然后把后续字符都转换成为double型的值。如果endptr不是空指针,那么strtod就修改endptr指向的对象,从而使endptr指向第一个剩余字符。如果没有发现double型的值,或者有错误的格式,那么strtod函数把nptr存储到endptr指向的对象中。如果要表示的数过大或者过小,函数就把ERANGE存储到errno中。


分离符号

直接用switch语句就可以了。

一、只有加减

只有加减,计算顺序是从左至右,这时表达式总是以数字开始,数字和符号间隔分布。先得到第一个数字,然后进入循环,一次读一个符号一个数字,读完就计算当前答案,直到字符串结尾。流程:读第一个数字,(读运算符,读第二个数字,判断运算符做计算)括号内不断循环,直到字符串结尾。

/* 计算 */int Calculate( char expre[], int len ){    char operate,          //运算符号         sign;             //正负号    int  num1, num2,       //操作数         i = 0;    /* 读入第一个数 */    num1 = 0;    sign = 1;    if ( '-' == expre[i] ) {        sign = -1;        ++i;    }    for ( i; expre[i] >= '0' && expre[i] <= '9'; ++i ) {        num1 *= 10;        num1 += expre[i] - '0';    }    num1 *= sign;    while ( i < len ) {        operate = expre[i++];        /* 运算符号 */        /* 第二个数 */        num2 = 0;        sign = 1;        if ( '-' == expre[i] ) {            sign = -1;            ++i;        }        for ( i; expre[i] >= '0' && expre[i] <= '9'; ++i ) {            num2 *= 10;            num2 += expre[i] - '0';        }        num2 *= sign;        /* 运算 */        if ( '+' == operate ) {            num1 = num1 + num2;        }        else if ( '-' == operate ) {            num1 = num1 - num2;        }    }    return num1;}

二、加上括号
注意到括号内仍然是一个表达式,因此用递归算出括号内子表达式的结果,左括号递归,右括号返回。括号可以迭代,函数递归也可以多层,但是注意括号配对。左括号左边可能有运算符号,右边一定是数字(也有可能有负号),因此判断左括号在读数字之前。同样,判断右括号在读数字之后。流程:
判断左括号,读第一个数字,(判断右括号,读运算符,判断左括号,读第二个数字,判断运算符做计算)括号内不断循环,直到字符串结尾。
int Calculate( char * begin ){    char operate,          //运算符号         sign;             //正负号    int  num1, num2;       //操作数    express = begin;    /* 第一个数 */    num1 = 0;    if ( '(' == *express ) {              /* 左括号 */        num1 = Calculate( ++express );    /* 递归 */    }    else {        sign = 1;        if ( '-' == *express ) {          /* 负号 */            sign = -1;            ++express;        }        while ( *express >= '0' && *express <= '9' ) {            num1 = 10 * num1 + *(express++) - '0';        }        num1 *= sign;    }    while ( *express ) {        operate = *(express++);           /* 运算符号 */        if ( ')' == *express )  {          /* 遇到右括号返回 */            ++express;            return num1;        }        /* 第二个数 */        num2 = 0;        if ( '(' == *express ) {            num2 = Calculate( ++express );        }        else {            sign = 1;            if ( '-' == *express ) {                sign = -1;                ++express;            }            while ( *express >= '0' && *express <= '9' ) {                num2 = 10 * num2 + *(express++) - '0';            }            num2 *= sign;        }        /* 运算 */        if ( '+' == operate ) {            num1 = num1 + num2;        }        else if ( '-' == operate ) {            num1 = num1 - num2;        }    }<pre name="code" class="cpp">    return num1;}

三、有乘除运算

乘除是高级运算,且在当前是最高级,因此当遇到乘除号的时候,直接计算结果。加减是低级运算,遇到加减号的时候,还要检查后一个运算符:若是加减,则进行前面的计算;若是乘除,继续读下一个数字,先进行后面的计算。最长表达式类似于a+b*c,需要保存三个数字,两个运算符。流程:判断左括号,读第一个数字,(判断右括号,读运算符,判断左括号,读第二个数字,判断运算级别,级别高直接计算,级别低进入下一层循环(判断右括号,是则计算返回,否则继续读运算符,判断运算级别,低级进行左边运算,否则判断左括号,读第三个数字,做计算))两层循环,直到字符串结尾。这一阶段最难的部分就是判断优先级,情况很多,在不重复不遗漏的时候要注意简化代码,合并相同部分,尽量使得计算阶段放在一起

double Calculate( char * begin ){    char   oper1, oper2;           //运算符号    double num1, num2, num3;       //操作数    express = begin;    /* 第一个数 */    num1 = 0;    if ( '(' == *express ) {                   /* 左括号递归 */        num1 = Calculate( ++express );    }    if ( *express >= '0' && *express <= '9' || '-' == *express ) {        num1 = GetAReal( express );    }    while ( *express ) {        if ( ')' == *express )  {             /* 右括号返回 */            ++express;            return num1;        }        oper1 = *(express++);                 /* 运算符号 */        /* 第二个数 */        num2 = 0;        if ( '(' == *express ) {            num2 = Calculate( ++express );        }        if ( *express >= '0' && *express <= '9' || '-' == *express ) {            num2 = GetAReal( express );        }        /* 运算 */        /* 乘除直接计算 */        if ( '*' == oper1 ) {            num1 *= num2;            continue;        }        else if ( '/' == oper1 ) {            num1 /= num2;            continue;        }        /* 加减判断优先级 */        while ( *express ) {            /* 在括号内直接计算返回 */            if ( ')' == *express ) {                ++express;                switch ( oper1 )                {                    case '+':                        return num1+num2;                    case '-':                        return num1-num2;                    case '*':                        return num1*num2;                    case '/':                        return num1/num2;                    default:                        printf("ERROR");                        return 0;                }            }            if ( '+' == *express || '-' == *express ) {                if ( '+' == oper1 ) {                    num1 += num2;                    break;                }                else if ( '-' == oper1 ) {                    num1 -= num2;                    break;                }            }            oper2 = *(express++);                 /* 运算符号 */            /* 第三个数 */            num3 = 0;            if ( '(' == *express ) {                num3 = Calculate( ++express );            }            if ( *express >= '0' && *express <= '9' || '-' == *express ) {                num3 = GetAReal( express );            }            if ( '*' == oper2 ) {                num2 *= num3;                continue;            }            else if ( '/' == oper2 ) {                num2 /= num3;                continue;            }        }        /* 到式子末尾 */        if ( !( *express )) {            if ( '+' == oper1 ) {                num1 += num2;                continue;            }            else if ( '-' == oper1 ) {                num1 -= num2;                continue;            }        }    }    return num1;}

四、有更高级的运算

在前面已经观察到,仅仅两级运算加上括号,情况已经非常复杂,如果增加运算级别,情况会增多到难以编写,考虑使用数据结构简化分析过程。使用树保存表达式的结构,通过搜索计算出结果。也可以用栈,通过运算级别的不同实现入栈出栈,数据全部弹出的时候,就得到了结果。

用栈实现:

暂缺


用树实现:

建立一棵二叉树,树的深度不知,只能使用链式存储,每个结点有三个指针,指向父节点,左子结点和右子节点。树的叶结点存储数字,其余节点保存运算符号。先序遍历计算,计算完把值存在父节点中,删除父节点中的符号,左子结点和右子节点,剩最后一个结点时就是计算结果。


不足之处是容错性不好,对于输入要求很高,不过自己是够用了。


0 0
原创粉丝点击