最近学习C++的字符串使用,突然想起了通过解析字符串实现计算任意表达式。C++主流的解析方式就是使用stack进行数据解析,无意间发现一篇通过递归的方式进行表达式解析的帖子,算法确实很赞 (原贴地址:【算法分享】纯VB解析四则运算文字表达式。支持任意个数括号嵌套 )那么动手吧,于是我按照作者的代码设计思路,移植成C++代码,使用VC6.0进行编译测试,效果良好。用到的知识点也不少,包含了STL的string、vector、sstream的对象使用,模板函数,递归算法使用等。有兴趣的朋友可以一起研究一下。
#pragma warning (disable: 4786) //屏蔽VC6下STL的报警 -_-b
#include <math.h>
#include <string>
#include <algorithm> //STL算法库头文件
#include <sstream>
#include <iostream>
#include <vector>
#include <iomanip>
using namespace std;
//宏定义,字符串转数值
#define ATOF(str) atof((str).c_str())
//转小写
string ToLowerString(string str)
{
transform(str.begin(), str.end(), str.begin(), (int (*)(int))tolower);
return str;
}
//转大写
string ToUpperString(string str)
{
transform(str.begin(), str.end(), str.begin(), (int (*)(int))toupper);
return str;
}
//字符替换
void string_Replace(string &str,const string &fstr,const string &rep)
{
string::size_type pos = 0;
string::size_type a = fstr.length();
string::size_type b = rep.length();
if (b == 0)
while ((pos = str.find(fstr, pos)) != string::npos)
{
str.erase(pos, a);
}
else
while ((pos = str.find(fstr, pos)) != string::npos)
{
str.replace(pos, a, rep);
pos += b;
}
}
//字符串分隔
vector<string> Split(const string str, const string pattern, int count = 0)
{
vector<string> ret;
if (pattern.empty()) return ret;
size_t start = 0, index = str.find(pattern, 0);
int control = count;
while (index != str.npos && (count == 0 || (control--) > 1))
{
if (start != index)
ret.push_back(str.substr(start, index - start));
else
ret.push_back(string(""));
start = index + pattern.length();
index = str.find(pattern, start);
}
if (!str.substr(start).empty())
ret.push_back(str.substr(start));
else
for (int i = 0; i < count - ret.size(); i++)
ret.push_back(string(""));
return ret;
}
//判断是否为数值
bool IsNumeric(const string& s)
{
int lk = 0, rk = 0;
for (string::size_type i = 0; i < s.size(); i++)
{
if (s[i] == '(')
{
lk++;
continue;
}
if (s[i] == ')')
{
rk++;
continue;
}
if (s[i] == '.')
continue;
if (s[i] < '0' || s[i] > '9')
{
return false;
}
}
if (lk != rk) return false;
return true;
}
//符号判断
int sgn(double x)
{
int ret;
if (x > 0) ret = 1;
if (x < 0) ret = -1;
if (x == 0) ret = 0;
return ret;
}
//任意数值转换为string对象
template<typename T>
string toString(const T& t)
{
ostringstream oss;
//设置转换精度
oss.setf(ios::fixed,ios::floatfield);
oss.precision(14);
oss << t;
return oss.str();
}
//字符串逆序
string StrReverse(const string &ss)
{
return string(ss.rbegin(),ss.rend()); //VS2010 支持反向迭代器构造函数,VC6不支持
}
double Eval(string s)
{
double ret;
char op;
s = ToLowerString(s); //转为小写
string_Replace(s," ", ""); //去空格
string_Replace(s, "--", "+"); //负负为正
string_Replace(s, "mod", "%"); //取余数符号
string ss("(&-+%\\*/^"); //基础数学运算符 越靠后优先级高
string fc[] = {"Abs", "Atn", "Cos", "Exp", "Fix", "Int", "Log", "Rnd", "Sgn", "Sin", "Sqr", "Tan"};
for (int i = 0; i < sizeof(fc) / sizeof(fc[0]); i++)
{
vector<string> hs = Split(s, ToLowerString(fc[i]));
if (hs.size() == 2) //找到函数表达式
{
if ((IsNumeric(hs[1]) && hs[0].size() < 2) || (hs[0].empty() && hs[1].empty()))
{
string_Replace(hs[1], "(", "");
string_Replace(hs[1], ")", "");
switch (i)
{
case 0:
ret = abs(ATOF(hs[1]));
break;
case 1:
ret = atan(ATOF(hs[1]));
break;
case 2:
ret = cos(ATOF(hs[1]));
break;
case 3:
ret = exp(ATOF(hs[1]));
break;
case 4:
ret = floor(ATOF(hs[1]));
break;
case 5:
ret = int(ATOF(hs[1]));
break;
case 6:
ret = log(ATOF(hs[1]));
break;
case 7:
ret = rand() * ATOF(hs[1]);
break;
case 8:
ret = sgn(ATOF(hs[1]));
break;
case 9:
ret = sin(ATOF(hs[1]));
break;
case 10:
ret = sqrt(ATOF(hs[1]));
break;
case 11:
ret = tan(ATOF(hs[1]));
break;
default:
break;
}
ret = ATOF(hs[0] + toString(ret));
cout << hs[0] << fc[i] << "(" << hs[1] << ")=" << ret << endl;
return ret;
}
}
}
if (IsNumeric(s) || s.empty())
ret = ATOF(s);
else
for (string::iterator it = ss.begin(); it != ss.end(); it++) //基础运算符解析
{
op = *it;
if (s.find(op, 0) != string::npos)
{
vector<string> fs = Split(StrReverse(s), toString(op), 2);
while (! IsNumeric(fs[1].substr(0, 1)) && (op == '-' || op == '+'))
{
if (fs[1].find(op, 0) != string::npos)
{
vector<string> ms = Split(fs[1], toString(op), 2);
fs[0] = fs[0] + op + ms[0];
fs[1] = ms[1];
}
else
op = '\0';
}
fs[0] = StrReverse(fs[0]);
fs[1] = StrReverse(fs[1]);
vector<string> ns = Split(fs[0], string(")"), 2);
switch (op)
{
case '(':
cout << s << endl;
ret = Eval(fs[1] + toString(Eval(ns[0])) + ns[1]);
return ret;
break;
case '&':
ret = ATOF(toString(Eval(fs[1])) + toString(Eval(fs[0])));
break;
case '+':
ret = Eval(fs[1]) + Eval(fs[0]);
break;
case '-':
ret = Eval(fs[1]) - Eval(fs[0]);
break;
case '*':
ret = Eval(fs[1]) * Eval(fs[0]);
break;
case '/':
ret = Eval(fs[1]) / Eval(fs[0]);
break;
case '\\':
ret = int(Eval(fs[1]) / Eval(fs[0]));
break;
case '%':
ret = static_cast<int>(Eval(fs[1])) % static_cast<int>(Eval(fs[0]));
break;
case '^':
ret = pow(Eval(fs[1]), Eval(fs[0]));
break;
}
if (op != '\0')
{
cout << fs[1] << op << fs[0] << "=" << ret << endl;
return ret;
}
}
}
return ret;
}
int main(int argc, char* argv[])
{
//设置输出精度(全局有效)
cout<<setiosflags(ios::fixed)<<setprecision(10);
string ss = "235*(34-52/3)^2+sin(45*3.1415926/180)";
double ret = Eval(ss) ;
cout << ss << "=" << ret << endl;
return 0;
}