由于自身思维不够活跃,思考问题逻辑不够清晰,所以小弟的师傅给小弟我布置了个作业,字符串表达式求值,以此希望达到锻炼我思维逻辑能力的目的。
历时14天,完成作业,相关知识以及技术并不高深,目的在于锻炼逻辑思维能力。在此也想跟有相关需要的同学们分享下解题思路,有不足之处也希望大家不吝赐教,指点出来。谢谢。
解决该问题时首先要解决判断运算符优先级问题,后来了解到后缀表达式(即逆波兰表达式)后,决定先将表达式分解成逆波兰表达式 ,然后再根据每个运算符取出数字进行相应的运算。计算到最后即为表达式的值
涉及string字符串、动态数组vector、逆波兰表达式(网上由相应的解析,书上并没有出现),迭代器(主要起取出以及作转换范围使用)的相关知识。
低配版(仅支持0-9的正整数计算,可以计算的运算符包含+ - * / % ^ & |)
代码如下:
1 //原型: double Exper(const char * expr); 2 //使用: double retval = Expr("1*2+(10/4)-3^1234"); 3 #include<iostream> 4 #include<string> 5 #include<sstream> 6 #include<vector> 7 #include<stack> 8 #include<cstdio> 9 using namespace std; 10 11 bool gettruefalse(char a) //判断当前符号是否为符号 12 { 13 if (a == '+' || a == '-' || a == '*' || a == '/' || a == '%' || \ 14 a == '^' || a == '|' || a == '&' || a == '(' || a == ')') 15 return true; 16 else 17 return false; 18 } 19 20 int getpriority(char c) //判断当前运算符的优先级 21 { 22 int temp; 23 if (c == '(') 24 temp = 6; 25 else if (c == '*' || c == '/' || c == '%') 26 temp = 5; 27 else if (c == '+' || c == '-') 28 temp = 4; 29 else if (c == '&') 30 temp = 3; 31 else if (c == '^') 32 temp = 1; 33 else if (c == '|') 34 temp = 1; 35 else if (c == ')') //如果为')',则一直将符号取出放入新字符串,直到遇到'('(6) 36 temp = 0; 37 return temp; 38 } 39 40 string getnbl(string str) //将原字符串转为逆波兰形式的字符串 41 { 42 string ok;//用于存放转换成后缀表达式的容器 43 vector<char>flag;//用于存放转换时的符号容器 44 45 while (str.length() != 0)//将旧字符串转换完成前,无限循环 46 { 47 for (int a = 0; a < str.length(); a++) //遍历字符串 48 { 49 if (gettruefalse(str[a])) //如果当前字符为符号 50 { 51 //如果符号容器为空,或者当前符号优先级大于符号容器优先级,或者符号容器最后一个符号为(,压入 52 if (flag.size() == 0 || (getpriority(str[a]) > getpriority(*(flag.end() - 1))) || getpriority(*(flag.end() - 1)) == 6) 53 { 54 flag.push_back(str[a]);//将当前符号放入符号容器 55 str.erase(str.begin(), str.begin() + 1); //截断旧字符串中的符号 56 break;//跳出for循环,重新遍历 57 } 58 else if (getpriority(str[a]) == 0) //是)吗 59 { 60 str.erase(str.begin(), str.begin() + 1);//删掉旧字符串的),并将符号容器的符号移到新字符串,直到遇到( 61 while (getpriority(*(flag.end() - 1)) != 6)//遇到(之前 无限弹出容器里的符号 62 { 63 ok += *(flag.end() - 1);//将符号容器的符号添加到新字符 64 flag.erase(flag.end() - 1);//将刚被添加到新字符的字符删去 65 } 66 if ((getpriority(*(flag.end() - 1)) == 6))//将(去掉 67 { 68 flag.erase(flag.end() - 1); 69 break;//跳到开头while循环处 70 } 71 } 72 else if (getpriority(str[a]) <= getpriority(*(flag.end() - 1))) //当前符号优先级小于或等于符号容器最后一个符号 73 { 74 ok += *(flag.end() - 1);//将符号容器的符号添加到新字符 75 flag.erase(flag.end() - 1);//将刚被添加到新字符的字符删去 76 break; 77 } 78 } 79 else //如果当前字符不为符号(即为数字或小数点) 80 { 81 ok += str[a];//将该数字转入新的字符串 82 str.erase(a, 1); //在旧字符串中删除该数字 83 break;//跳出for循环,重新开始遍历 84 } 85 } 86 } 87 //旧字符串清空后,将临时存放点的字符串依次取出放入新字符中 88 while (flag.size() != 0) 89 { 90 ok += *(flag.end() - 1); 91 flag.erase(flag.end() - 1); 92 } 93 return ok; 94 } 95 96 int jisuan(int a, char b, int c) //根据符号取数字进行相应的计算 97 { 98 int num = 0;//计算结果 99 if (b == '+') 100 num = a + c; 101 else if (b == '-') 102 num = a - c; 103 else if (b == '*') 104 num = a * c; 105 else if (b == '/') 106 num = a / c; 107 else if (b == '%') 108 num = a % c; 109 else if (b == '^') 110 num = a ^ c; 111 else if (b == '&') 112 num = a & c; 113 else if (b == '|') 114 num = a | c; 115 else if (b == 'M') 116 num = a && c; 117 else if (b == 'N') 118 num = a || c; 119 return num; 120 } 121 122 int getcount(string nbl) //将逆波兰形式的字符串进行转换为数字类型然后进行符号的运算 123 { 124 vector<int>nums; 125 int a = 0; 126 int answer; //存放结果 127 string zhuanhuan; 128 while (a < nbl.size()) 129 { 130 if (gettruefalse(nbl[a]))//如果为符号 131 { //从数字容器中去除倒数第二的数作为A,最后一个数作为B,执行该符号的运算 132 answer = jisuan(*(nums.end() - 2), nbl[a], *(nums.end() - 1)); 133 nums.erase(nums.end() - 2, nums.end()); 134 nums.push_back(answer); 135 a++;//遍历下一个字符 136 } 137 else //数字时 压入数字容器 138 { 139 zhuanhuan += nbl[a]; 140 answer = atof(zhuanhuan.c_str()); 141 nums.push_back(answer); 142 zhuanhuan.clear(); 143 a++; 144 } 145 } 146 return *(nums.end() - 1); //容器最后一位数即为结果 147 } 148 149 int main() 150 { 151 char ch[100]; 152 cout << "输入你要计算的字符串表达式(仅支持0-9的正整数运算):" << endl; 153 gets_s(ch); 154 cout << "字符串的原内容为:" << ch << endl; 155 156 string str(ch);//用于进行转换操作的字符串 157 string nbl;//用于存放转换成后缀表达式的容器 158 nbl = getnbl(str);//调用函数 将字符串转为逆波兰字符串 159 cout << "转为逆波兰表达式后:" << nbl << endl; 160 int num = getcount(nbl); 161 cout << "字符串:" << ch << "的结果为:" << num << endl; 162 163 system("pause"); 164 fflush(stdin); 165 return 0; 166 }`
完全版(此时可以计算正负的整数(不支持小数点),运算符也包含+ - * / % & && ^ | ||):
1 //原型: double Exper(const char * expr); 2 //使用: double retval = Expr("1*2+(10/4)-3^1234"); 3 #include<iostream> 4 #include<string> 5 #include<vector> 6 #include<cstdio> 7 using namespace std; 8 9 int gettruefalse(char a) //判断当前符号是否为运算符或数字 10 { 11 if (a == '1' || a == '2' || a == '3' || a == '4' || a == '5' || \ 12 a == '6' || a == '7' || a == '8' || a == '9' || a == '0') 13 return 2; 14 else if (a == '+' || a == '-' || a == '*' || a == '/' || a == '%' || \ 15 a == '^' || a == '|' || a == '&' || a == '(' || a == ')' || a == 'M' || a == 'N') 16 return 1; 17 else 18 return 0; 19 } 20 21 int getpriority(char c) //优先级排列 22 { 23 int temp; 24 if (c == '(') 25 temp = 8; 26 else if (c == '*' || c == '/' || c == '%') 27 temp = 7; 28 else if (c == '+' || c == '-') 29 temp = 6; 30 else if (c == '&') 31 temp = 5; 32 else if (c == '^') 33 temp = 4; 34 else if (c == '|') 35 temp = 3; 36 else if (c == 'M') //等同于&& 37 temp = 2; 38 else if (c == 'N') //等同于|| 39 temp = 1; 40 else if (c == ')') //如果为')',则一直将符号取出放入新字符串,直到遇到'('(6) 41 temp = 0; 42 return temp; 43 } 44 45 void getvariant(string &str) 46 { 47 int a = 0;//用于遍历 48 for (a = 0; a < str.size(); a++) //将原字符串中的-(符号)用相应的标识符替换 49 { 50 if ((a == 0) && (str[a] == '-')) 51 { 52 str[a] = '.'; 53 } 54 else if ((str[a] == '-') && (gettruefalse(str[a - 1]) == 1) && (str[a - 1] != ')')) //找到-并确认当前是否为符号 55 { 56 str[a] = '.'; 57 } 58 } 59 for (a = 0; a < str.size(); a++) //将原字符串中的||、&&用相应的标识符替代 60 { 61 if (str[a] == '|' && str[a + 1] == '|') 62 { 63 str[a + 1] = 'N'; 64 str.erase(a, 1); 65 } 66 if (str[a] == '&' && str[a + 1] == '&') 67 { 68 str[a + 1] = 'M'; 69 str.erase(a, 1); 70 } 71 } 72 } 73 74 string getnbl(string str) 75 { 76 getvariant(str);//调用函数用标记符替换字符串中的||、&&、-(负号) 77 string ok;//用于存放转换成后缀表达式的容器 78 vector<char>flag;//用于存放转换时的符号容器 79 int a = 0;//用于作为遍历字符串的下标 80 int b = a;//!b作为开始遍历数字的起始点 81 82 while (a <str.length())//遍历整个字符串 83 { 84 if ((gettruefalse(str[a])) == 1)//当前字符为符号 85 { 86 //符号容器为空或者当前符号优先级大于容器末尾符号优先级,或当前容器末尾为(,压入容器 87 if (flag.size() == 0 || (getpriority(str[a]) > getpriority(*(flag.end() - 1))) \ 88 || *(flag.end() - 1) == '(') 89 { 90 flag.push_back(str[a]); 91 a++; 92 } 93 else if (str[a] == ')') //如果为) 不压入,弹出容器符号 直到遇到( 94 { 95 while (*(flag.end() - 1) != '(')//当前符号不为(,则一直弹出容器符号 96 { 97 ok += *(flag.end() - 1); 98 ok += ' '; //每个符号以空格作为间隔 99 flag.erase(flag.end() - 1); 100 } 101 flag.erase(flag.end() - 1);//删掉容器中的( 102 a++;//跳过')'符号 103 } 104 else if (getpriority(str[a]) <= getpriority(*(flag.end() - 1)))//当前符号优先级小于或等于符号容器最后一个符号 105 { 106 ok += *(flag.end() - 1);//将符号容器的符号添加到新字符 107 ok += ' '; 108 flag.erase(flag.end() - 1);//将刚被添加到新字符的字符删去 109 } 110 } 111 else //不为符号,则遍历字符串找到符号 112 { 113 b = a; //记录当前开始遍历的位置 114 while(a<str.size()) 115 { 116 if ( (gettruefalse(str[a]) == 1)) //当当前字符为符号 117 { 118 ok.append(str.begin() + b, str.begin() + a);//将遍历的起始位置到发现符号的位置的所有字符拼接到新的字符串,并加上空格作为间隔 119 ok += ' '; 120 break; //跳出for循环 重新进行判断 121 } 122 else if ((a==(str.size()-1)) && (gettruefalse(str[a]) == 2)) //已经遍历到最后一个字符了 且最后一个字符为数字 123 { 124 a++; 125 ok.append(str.begin() + b, str.begin() + a);//将遍历的起始位置到发现符号的位置的所有字符拼接到新的字符串,并加上空格作为间隔 126 ok += ' '; 127 break; //跳出for循环 重新进行判断 128 } 129 a++; 130 } 131 } 132 } 133 while (flag.size() != 0) //遍历完原字符串后,将符号容器的符号全部弹出 134 { 135 ok += *(flag.end() - 1); 136 ok += ' '; //每个符号以空格作为间隔 137 flag.erase(flag.end() - 1); 138 } 139 return ok; 140 } 141 142 int getanswer(int a, char b, int c) 143 { 144 int num = 0;//计算结果 145 if (b == '+') 146 num = a + c; 147 else if (b == '-') 148 num = a - c; 149 else if (b == '*') 150 num = a * c; 151 else if (b == '/') 152 num = a / c; 153 else if (b == '%') 154 num = a % c; 155 else if (b == '^') 156 num = a ^ c; 157 else if (b == '&') 158 num = a & c; 159 else if (b == '|') 160 num = a | c; 161 else if (b == 'M') 162 num = a && c; 163 else if (b == 'N') 164 num = a || c; 165 return num; 166 167 } 168 169 int getcount(string nbl) //计算逆波兰表达式的 170 { 171 int num=0;//结果 172 string zhuanhuan; 173 vector<int> nums;//数字容器 174 int a = 0;//遍历字符串下标 175 int b = a;//记录开始遍历时的下标 176 while (a < nbl.size()) 177 { 178 if (gettruefalse(nbl[a])==1) //当前为符号 179 { 180 if (((nbl[a] == '/') || (nbl[a] == '%')) && (*(nums.end() - 1) == 0)) //计算除法和求余时,如果被除数为0,报错 181 { 182 cout << "除数不能为0,程序退出" << endl; 183 system("pause"); 184 exit(-1); 185 } 186 187 num = getanswer(*(nums.end() - 2), nbl[a], *(nums.end() - 1));//弹出数字容器的2个数字 并根据符号去计算这2个数字 188 nums.erase(nums.end() - 2, nums.end());//计算后去掉这2个数字,并将结果重新放入容器中 189 nums.push_back(num); 190 a++;//遍历下一个字符 191 } 192 else if (nbl[a] == ' ') 193 { 194 a++; 195 } 196 else 197 { 198 b = a;//记录开始找空格的位置 199 while (a<nbl.size()) 200 { 201 if (nbl[a] == ' ')//找到空格 202 { 203 if (nbl[b] == '.') //如果该数为负数(起始处为.) 204 { 205 nbl[b] = '-'; //将小数点转换为负号 206 } 207 zhuanhuan.append(nbl.begin() + b, nbl.begin() + a); 208 num = atof(zhuanhuan.c_str()); //将该字符串转换为数字 并压入数字容器中 209 zhuanhuan.clear(); 210 nums.push_back(num); 211 break; 212 } 213 a++; 214 } 215 216 } 217 } 218 num = *(nums.end() - 1); 219 return num; 220 } 221 222 int main() 223 { 224 char ch[100]; 225 cout << "输入你要计算的字符串表达式(仅支持整数运算):" << endl; 226 gets_s(ch); 227 string str(ch);//用于进行转换操作的字符串 228 int s = 0; 229 while (s < str.size())//检查用户输入表达式是否正确 230 { 231 if (gettruefalse(str[s]) == 0) 232 { 233 cout << "字符串表达式输入错误" << endl; 234 system("pause"); 235 return 0; 236 } 237 s++; 238 } 239 string nbl;//用于存放转换成后缀表达式的容器 240 nbl = getnbl(str);//调用函数 将字符串转为逆波兰字符串 241 int num;//表达式结果 242 num = getcount(nbl); 243 cout << "转为逆波兰表达式后:" << nbl << ",.(-)、M(&&)、N(|| )为标记符"<<endl; 244 cout << "字符串表达式:" << ch << "的结果为:" << num << endl; 245 cout << "结果为:" << num << endl; 246 system("pause"); 247 fflush(stdin); 248 return 0; 249 }
运行结果: