關於實現字符串表達式求值


由於自身思維不夠活躍,思考問題邏輯不夠清晰,所以小弟的師傅給小弟我布置了個作業,字符串表達式求值,以此希望達到鍛煉我思維邏輯能力的目的。
歷時14天,完成作業,相關知識以及技術並不高深,目的在於鍛煉邏輯思維能力。在此也想跟有相關需要的同學們分享下解題思路,有不足之處也希望大家不吝賜教,指點出來。謝謝。

解決該問題時首先要解決判斷運算符優先級問題,后來了解到后綴表達式(即逆波蘭表達式)后,決定先將表達式分解成逆波蘭表達式 ,然后再根據每個運算符取出數字進行相應的運算。計算到最后即為表達式的值
涉及string字符串、動態數組vector、逆波蘭表達式(網上由相應的解析,書上並沒有出現),迭代器(主要起取出以及作轉換范圍使用)的相關知識。

低配版(僅支持0-9的正整數計算,可以計算的運算符包含+ - * / % ^ & |)
代碼如下:`//原型: double Exper(const char * expr);
//使用: double retval = Expr("1*2+(10/4)-3^1234");
#include<iostream>
#include<string>
#include<sstream>
#include<vector>
#include<stack>
#include<cstdio>
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<char>flag;//用於存放轉換時的符號容器

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>nums;
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<iostream>
#include<string>
#include<vector>
#include<cstdio>
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<char>flag;//用於存放轉換時的符號容器
int a = 0;//用於作為遍歷字符串的下標
int b = a;//!b作為開始遍歷數字的起始點

while (a <str.length())//遍歷整個字符串
{
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<str.size())
{
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> nums;//數字容器
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<nbl.size())
{
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(|| )為標記符"<<endl;
cout << "字符串表達式:" << ch << "的結果為:" << num << endl;
cout << "結果為:" << num << endl;
system("pause");
fflush(stdin);
return 0;
}
```
運行結果:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM