由于自身思维不够活跃,思考问题逻辑不够清晰,所以小弟的师傅给小弟我布置了个作业,字符串表达式求值,以此希望达到锻炼我思维逻辑能力的目的。
历时14天,完成作业,相关知识以及技术并不高深,目的在于锻炼逻辑思维能力。在此也想跟有相关需要的同学们分享下解题思路,有不足之处也希望大家不吝赐教,指点出来。谢谢。
解决该问题时首先要解决判断运算符优先级问题,后来了解到后缀表达式(即逆波兰表达式)后,决定先将表达式分解成逆波兰表达式 ,然后再根据每个运算符取出数字进行相应的运算。计算到最后即为表达式的值
涉及string字符串、动态数组vector、逆波兰表达式(网上由相应的解析,书上并没有出现),迭代器(主要起取出以及作转换范围使用)的相关知识。
低配版(仅支持0-9的正整数计算,可以计算的运算符包含+ - * / % ^ & |)
代码如下:`//原型: double Exper(const char * expr);
//使用: double retval = Expr("1*2+(10/4)-3^1234");
#include
#include
#include
#include
#include
#include
using namespace std;
bool gettruefalse(char a) //判断当前符号是否为符号
{
if (a == '+' || a == '-' || a == '*' || a == '/' || a == '%' || \
a == '^' || a == '|' || a == '&' || a == '(' || a == ')')
return true;
else
return false;
}
int getpriority(char c) //判断当前运算符的优先级
{
int temp;
if (c == '(')
temp = 6;
else if (c == '*' || c == '/' || c == '%')
temp = 5;
else if (c == '+' || c == '-')
temp = 4;
else if (c == '&')
temp = 3;
else if (c == '^')
temp = 1;
else if (c == '|')
temp = 1;
else if (c == ')') //如果为')',则一直将符号取出放入新字符串,直到遇到'('(6)
temp = 0;
return temp;
}
string getnbl(string str) //将原字符串转为逆波兰形式的字符串
{
string ok;//用于存放转换成后缀表达式的容器
vector
while (str.length() != 0)//将旧字符串转换完成前,无限循环
{
for (int a = 0; a < str.length(); a++) //遍历字符串
{
if (gettruefalse(str[a])) //如果当前字符为符号
{
//如果符号容器为空,或者当前符号优先级大于符号容器优先级,或者符号容器最后一个符号为(,压入
if (flag.size() == 0 || (getpriority(str[a]) > getpriority(*(flag.end() - 1))) || getpriority(*(flag.end() - 1)) == 6)
{
flag.push_back(str[a]);//将当前符号放入符号容器
str.erase(str.begin(), str.begin() + 1); //截断旧字符串中的符号
break;//跳出for循环,重新遍历
}
else if (getpriority(str[a]) == 0) //是)吗
{
str.erase(str.begin(), str.begin() + 1);//删掉旧字符串的),并将符号容器的符号移到新字符串,直到遇到(
while (getpriority(*(flag.end() - 1)) != 6)//遇到(之前 无限弹出容器里的符号
{
ok += *(flag.end() - 1);//将符号容器的符号添加到新字符
flag.erase(flag.end() - 1);//将刚被添加到新字符的字符删去
}
if ((getpriority(*(flag.end() - 1)) == 6))//将(去掉
{
flag.erase(flag.end() - 1);
break;//跳到开头while循环处
}
}
else if (getpriority(str[a]) <= getpriority(*(flag.end() - 1))) //当前符号优先级小于或等于符号容器最后一个符号
{
ok += *(flag.end() - 1);//将符号容器的符号添加到新字符
flag.erase(flag.end() - 1);//将刚被添加到新字符的字符删去
break;
}
}
else //如果当前字符不为符号(即为数字或小数点)
{
ok += str[a];//将该数字转入新的字符串
str.erase(a, 1); //在旧字符串中删除该数字
break;//跳出for循环,重新开始遍历
}
}
}
//旧字符串清空后,将临时存放点的字符串依次取出放入新字符中
while (flag.size() != 0)
{
ok += *(flag.end() - 1);
flag.erase(flag.end() - 1);
}
return ok;
}
int jisuan(int a, char b, int c) //根据符号取数字进行相应的计算
{
int num = 0;//计算结果
if (b == '+')
num = a + c;
else if (b == '-')
num = a - c;
else if (b == '*')
num = a * c;
else if (b == '/')
num = a / c;
else if (b == '%')
num = a % c;
else if (b == '^')
num = a ^ c;
else if (b == '&')
num = a & c;
else if (b == '|')
num = a | c;
else if (b == 'M')
num = a && c;
else if (b == 'N')
num = a || c;
return num;
}
int getcount(string nbl) //将逆波兰形式的字符串进行转换为数字类型然后进行符号的运算
{
vector
int a = 0;
int answer; //存放结果
string zhuanhuan;
while (a < nbl.size())
{
if (gettruefalse(nbl[a]))//如果为符号
{ //从数字容器中去除倒数第二的数作为A,最后一个数作为B,执行该符号的运算
answer = jisuan(*(nums.end() - 2), nbl[a], *(nums.end() - 1));
nums.erase(nums.end() - 2, nums.end());
nums.push_back(answer);
a++;//遍历下一个字符
}
else //数字时 压入数字容器
{
zhuanhuan += nbl[a];
answer = atof(zhuanhuan.c_str());
nums.push_back(answer);
zhuanhuan.clear();
a++;
}
}
return *(nums.end() - 1); //容器最后一位数即为结果
}
int main()
{
char ch[100];
cout << "输入你要计算的字符串表达式(仅支持0-9的正整数运算):" << endl;
gets_s(ch);
cout << "字符串的原内容为:" << ch << endl;
string str(ch);//用于进行转换操作的字符串
string nbl;//用于存放转换成后缀表达式的容器
nbl = getnbl(str);//调用函数 将字符串转为逆波兰字符串
cout << "转为逆波兰表达式后:" << nbl << endl;
int num = getcount(nbl);
cout << "字符串:" << ch << "的结果为:" << num << endl;
system("pause");
fflush(stdin);
return 0;
}`
完全版(此时可以计算正负的整数(不支持小数点),运算符也包含+ - * / % & && ^ | ||):
```
//原型: double Exper(const char * expr);
//使用: double retval = Expr("1*2+(10/4)-3^1234");
#include
#include
#include
#include
using namespace std;
int gettruefalse(char a) //判断当前符号是否为运算符或数字
{
if (a == '1' || a == '2' || a == '3' || a == '4' || a == '5' || \
a == '6' || a == '7' || a == '8' || a == '9' || a == '0')
return 2;
else if (a == '+' || a == '-' || a == '*' || a == '/' || a == '%' || \
a == '^' || a == '|' || a == '&' || a == '(' || a == ')' || a == 'M' || a == 'N')
return 1;
else
return 0;
}
int getpriority(char c) //优先级排列
{
int temp;
if (c == '(')
temp = 8;
else if (c == '*' || c == '/' || c == '%')
temp = 7;
else if (c == '+' || c == '-')
temp = 6;
else if (c == '&')
temp = 5;
else if (c == '^')
temp = 4;
else if (c == '|')
temp = 3;
else if (c == 'M') //等同于&&
temp = 2;
else if (c == 'N') //等同于||
temp = 1;
else if (c == ')') //如果为')',则一直将符号取出放入新字符串,直到遇到'('(6)
temp = 0;
return temp;
}
void getvariant(string &str)
{
int a = 0;//用于遍历
for (a = 0; a < str.size(); a++) //将原字符串中的-(符号)用相应的标识符替换
{
if ((a == 0) && (str[a] == '-'))
{
str[a] = '.';
}
else if ((str[a] == '-') && (gettruefalse(str[a - 1]) == 1) && (str[a - 1] != ')')) //找到-并确认当前是否为符号
{
str[a] = '.';
}
}
for (a = 0; a < str.size(); a++) //将原字符串中的||、&&用相应的标识符替代
{
if (str[a] == '|' && str[a + 1] == '|')
{
str[a + 1] = 'N';
str.erase(a, 1);
}
if (str[a] == '&' && str[a + 1] == '&')
{
str[a + 1] = 'M';
str.erase(a, 1);
}
}
}
string getnbl(string str)
{
getvariant(str);//调用函数用标记符替换字符串中的||、&&、-(负号)
string ok;//用于存放转换成后缀表达式的容器
vector
int a = 0;//用于作为遍历字符串的下标
int b = a;//!b作为开始遍历数字的起始点
while (a
if ((gettruefalse(str[a])) == 1)//当前字符为符号
{
//符号容器为空或者当前符号优先级大于容器末尾符号优先级,或当前容器末尾为(,压入容器
if (flag.size() == 0 || (getpriority(str[a]) > getpriority(*(flag.end() - 1))) \
|| *(flag.end() - 1) == '(')
{
flag.push_back(str[a]);
a++;
}
else if (str[a] == ')') //如果为) 不压入,弹出容器符号 直到遇到(
{
while (*(flag.end() - 1) != '(')//当前符号不为(,则一直弹出容器符号
{
ok += *(flag.end() - 1);
ok += ' '; //每个符号以空格作为间隔
flag.erase(flag.end() - 1);
}
flag.erase(flag.end() - 1);//删掉容器中的(
a++;//跳过')'符号
}
else if (getpriority(str[a]) <= getpriority(*(flag.end() - 1)))//当前符号优先级小于或等于符号容器最后一个符号
{
ok += *(flag.end() - 1);//将符号容器的符号添加到新字符
ok += ' ';
flag.erase(flag.end() - 1);//将刚被添加到新字符的字符删去
}
}
else //不为符号,则遍历字符串找到符号
{
b = a; //记录当前开始遍历的位置
while(a
if ( (gettruefalse(str[a]) == 1)) //当当前字符为符号
{
ok.append(str.begin() + b, str.begin() + a);//将遍历的起始位置到发现符号的位置的所有字符拼接到新的字符串,并加上空格作为间隔
ok += ' ';
break; //跳出for循环 重新进行判断
}
else if ((a==(str.size()-1)) && (gettruefalse(str[a]) == 2)) //已经遍历到最后一个字符了 且最后一个字符为数字
{
a++;
ok.append(str.begin() + b, str.begin() + a);//将遍历的起始位置到发现符号的位置的所有字符拼接到新的字符串,并加上空格作为间隔
ok += ' ';
break; //跳出for循环 重新进行判断
}
a++;
}
}
}
while (flag.size() != 0) //遍历完原字符串后,将符号容器的符号全部弹出
{
ok += *(flag.end() - 1);
ok += ' '; //每个符号以空格作为间隔
flag.erase(flag.end() - 1);
}
return ok;
}
int getanswer(int a, char b, int c)
{
int num = 0;//计算结果
if (b == '+')
num = a + c;
else if (b == '-')
num = a - c;
else if (b == '*')
num = a * c;
else if (b == '/')
num = a / c;
else if (b == '%')
num = a % c;
else if (b == '^')
num = a ^ c;
else if (b == '&')
num = a & c;
else if (b == '|')
num = a | c;
else if (b == 'M')
num = a && c;
else if (b == 'N')
num = a || c;
return num;
}
int getcount(string nbl) //计算逆波兰表达式的
{
int num=0;//结果
string zhuanhuan;
vector
int a = 0;//遍历字符串下标
int b = a;//记录开始遍历时的下标
while (a < nbl.size())
{
if (gettruefalse(nbl[a])==1) //当前为符号
{
if (((nbl[a] == '/') || (nbl[a] == '%')) && (*(nums.end() - 1) == 0)) //计算除法和求余时,如果被除数为0,报错
{
cout << "除数不能为0,程序退出" << endl;
system("pause");
exit(-1);
}
num = getanswer(*(nums.end() - 2), nbl[a], *(nums.end() - 1));//弹出数字容器的2个数字 并根据符号去计算这2个数字
nums.erase(nums.end() - 2, nums.end());//计算后去掉这2个数字,并将结果重新放入容器中
nums.push_back(num);
a++;//遍历下一个字符
}
else if (nbl[a] == ' ')
{
a++;
}
else
{
b = a;//记录开始找空格的位置
while (a
if (nbl[a] == ' ')//找到空格
{
if (nbl[b] == '.') //如果该数为负数(起始处为.)
{
nbl[b] = '-'; //将小数点转换为负号
}
zhuanhuan.append(nbl.begin() + b, nbl.begin() + a);
num = atof(zhuanhuan.c_str()); //将该字符串转换为数字 并压入数字容器中
zhuanhuan.clear();
nums.push_back(num);
break;
}
a++;
}
}
}
num = *(nums.end() - 1);
return num;
}
int main()
{
char ch[100];
cout << "输入你要计算的字符串表达式(仅支持整数运算):" << endl;
gets_s(ch);
string str(ch);//用于进行转换操作的字符串
int s = 0;
while (s < str.size())//检查用户输入表达式是否正确
{
if (gettruefalse(str[s]) == 0)
{
cout << "字符串表达式输入错误" << endl;
system("pause");
return 0;
}
s++;
}
string nbl;//用于存放转换成后缀表达式的容器
nbl = getnbl(str);//调用函数 将字符串转为逆波兰字符串
int num;//表达式结果
num = getcount(nbl);
cout << "转为逆波兰表达式后:" << nbl << ",.(-)、M(&&)、N(|| )为标记符"<
cout << "结果为:" << num << endl;
system("pause");
fflush(stdin);
return 0;
}
```
运行结果: