当前位置:C++技术网 > 资讯 > 程序解析混合四则运算表达式

程序解析混合四则运算表达式

更新时间:2015-09-19 12:31:18浏览次数:1+次

       一年之前  模仿了Windows自带的计算器   自己写了个简易版本   只能实现基本的加减乘数、取余、次方、开根,而且只能分步计算 ,不能直接利用运算表达式直接求解 ,当时一直没思路,这一摆就是一年,今天又翻出来琢磨了下!

       分享我的几种思路:1.利用栈  把符号和数字单独分离   判断运算符的类别  取出不同的数字做不同的计算

    如  1+4/2*3-5+2*(3+4)

   数字栈  1 4 2 3 5 2 3 4

    符号栈 + / * - + * ( + )

从不同的角度思考了好久   若表达式十分复杂  那么解析的难度将增加  如 +前面有*   或者 * 前面有 (),而()内又有*和+

则又要增加更多的栈   不容易管理

 2. 直接解析字符串,按优先级顺序 边解析边计算

(1).考虑到负号和减号程序无法区分   则直接不考虑减号   凡是遇到减号  全部换成+-(加一个负数)

(2).首先去括号  把括号内的表达式计算出来  再次合并成新的字符串  若有多层括号  从最里层开始处理

(3).先计算^(次方),后计算乘除,最后计算加法

如  1+4/2*3-5+2*(3+4)

第一步  减法变加法    1+4/2*3+-5+2*(3+4)

第二步  去括号,计算结果合成新的表达式   1+4/2*3+-5+2*7

第三步 计算乘除法  1+6+-5+14

第四步 计算加法  16

 

按照之前的思路,我们打包成四个函数

bool isop(char op);//判断是否是运算符

void DealString(char* str);//去减号   减号变负号

void GetSimpleString(char* str);//去括号   括号内有表达式的调用GetResult() 得到结果再次合并成新的字符串后循环

bool GetSubString(char* str,char* op,double *result);//逐步化简     循环查找运算符  分离出前后两个数 根据运算符做不同的运算

double GetResult(char *str);//得到最终结果  按先次方后乘除最后加的运算顺序

 

那我们正式来写下代码

bool isop(char op)  //判断是否是运算符  是则返回true
 {
  if(op=='+' || op=='*' || op=='/' || op=='^') return true;
  return false;
 }

 

void DealString(char* str));//去减号   减号变负号
{
  int len=strlen(str);
  int num=0;//记录插入的+数目
 for(int i=len;i>0;i--)//循环查找-
 {
   if(str[i]=='-' && str[i-1]>='0' && str[i-1]<='9')  //若为-  同时前面一个字符为数字 把+插入其中
  {
    for (int j=len+num;j>i;j--)//字符串后移
   {
     str[j]=str[j-1];
    }
    str[i]='+';
    num++;
   }
  }
  printf("the after deal:%s\n",str);
 }


 void GetSimpleString(char* str));//去括号
{
  char substr[N]="";//字符子串  用于保存中间结果
 char leave[N]="";//保存处理后剩余的字符串
 double result=0.0;//保存中间计算结果
 while(1)
  {
   memset(substr,0,N);
   memset(leave,0,N);
   char *find=strchr(str,')');//查找第一个 )  为了优先匹配最内层括号
  if(find==NULL) break;//若无括号 则退出循环函数返回
  char *q=find;
   while(q>=str)//循环查找  取出最内层括号中的字符串表达式
  {
    if(*q=='(')
    {
     strncpy(substr,q+1,find-(q+1));//substr 中保存括号内的字符串表达式
    result=GetResult(substr);//得到中间结果
    sprintf(substr,"%f",result);
     if(q>str) //若(为不是整个字符串的首字符  则复制进剩余字符串中  若是首字符 则清空
     strncpy(leave,str,q-str);
     else 
      strcpy(leave,"");
     strcat(leave,substr);//连接中间结果
    if(*(find+1))
      strcat(leave,find+1);
     strcpy(str,leave);//添加后续字符串
    break;
    }
    q--;
   }
  }
  printf("the simple string:%s\n",str);
 }

 

double GetResult(char *str));//得到最终结果  按先次方后乘除最后加的运算顺序
{
  double result=0.0;
  if(GetSubString(str,"^",&result)) return result;//计算次方

// if(GetSubString(str,'/',&result)) return result;

// if(GetSubString(str,'*',&result)) return result;
  if(GetSubString(str,"*/",&result)) return result;

/*计算乘除  这里同时计算是为了防止由于改变了运算优先级而出现类似11/11*12  得到结果1/12 另一方面也是为了避免优先查找计算/ 时所造成的精度损失

*/
 // if(GetSubString(str,'-',&result)) return result;
  if(GetSubString(str,"+",&result)) return result;//计算加
 return 0.0;
 }

 

//逐步化简     循环查找运算符  分离出前后两个数 根据运算符做不同的运算
bool GetSubString(char* str,char* op,double *result)
 {
  char strnum1[N]="";
  char strnum2[N]="";
  char leave[N]="";
  char strresu[N]="";
  double num1=0.0,num2=0.0;
  char *q=NULL,*p=NULL;
  bool isLast=false;
  while(1)
  {
   memset(strnum1,0,N);
   memset(strnum2,0,N);
   memset(leave,0,N);
   memset(strresu,0,N);
   char *find=str;
   while(*find)//查找运算符
  {
    if(*find==op[0] || (op[1]!='\0' && *find==op[1])) break;  是否符合运算符
   find++;
   }
   if (*find=='\0') break;//若没有找到运算符,说明已计算完毕
  q=find;
   q--;
   while(q>=str)));//往前查找 分离得到第一个数
   {
    if(isop(*q)) 
    {
     strncpy(strnum1,q+1,find-(q+1
     num1=atof(strnum1);
     break;
    }
    else if(q==str)
    {
     strncpy(strnum1,q,find-q);
     num1=atof(strnum1);
     break;
    }
    q--;
   }
   p=find;
   p++;
   while(*p)//往后查找 分离得到第一个数
   {
    if( isop(*p)) 
    {
     strncpy(strnum2,find+1,p-(find+1));
     num2=atof(strnum2);
     break;
    }
    else if(*(p+1)=='\0')
    {
     strncpy(strnum2,find+1,p+1-(find+1));
     num2=atof(strnum2);
     p++;
     break;
    }
    p++;
   }
   switch(*find)//根据查找到的符号来确定计算
   {
   case '+':*result=num1+num2;break;
 //  case '-':*result=num1-num2;break;
   case '*':*result=num1*num2;break;
   case '/':*result=num1/num2;break;
   case '^':*result=pow(num1,num2);break;
   }
   sprintf(strresu,"%f",*result);//结果转换
   if(q!=str)
    strncpy(leave,str,q-str+1);
   else
    strcpy(leave,"");
   strcat(leave,strresu);
   if(*p!='\0')
   {
   strcat(leave,p);
   }
   if(q==str && *p=='\0') isLast=true;//若最终往前循环到首字符  往后循环到达尾字符  说明只有一级运算 则计算结果有效
   strcpy(str,leave);
  }
  return isLast;//返回该运算是否为最终结果
 }


 

 

到目前为止,解析函数已全部完毕,剩下的就是主函数调用来调试了!为了方便多次调试,我们设置了循环输入,以#结束程序

 

 

int main()
{
 char str[N]="";
 while(str[0]!='#')
 {
  memset(str,0,N);
  printf("please input the string of you wang to calc['#' to quit]:\n");
  scanf("%s",str);
  if (str[0]!='#')
  {
   DealString(str);
   GetSimpleString(str);
   double result=GetResult(str);
   printf("the result:%f\n",result);
  }
 }
 return 0;
}

由于使用的是人工输入,因此需要添加检验运算表达式是否合格(如符号重复,括号不匹配等),我的检验函数并不完善,就不在这做说明了,你可以自行添加!

由于水平有限及时间仓促,难免有错误或遗漏之处,同时该方法执行效率和内存使用一般,如果有更好的方法或建议,欢迎补充!

运行结果测试效果图: