zl程序教程

您现在的位置是:首页 >  后端

当前栏目

C++ 用正则regex和容器vector计算字符串算式的值

C++计算容器 字符串 正则 vector regex 算式
2023-09-14 09:01:28 时间

要求:科学记数法必须是(d e± i,e后不能省略+或-)运算符必须是(+、-、*、/、^,5种)
   暂未处理有括号的运算式;字符串也不能有运算符和数字(包括 . )之外的字符
步骤:先用正则表达式regex把字符串算式拆分成数和运算符,依次存放进容器vector,
   然后按运算符优先级别先后计算出中间结果,最后计算代数和。

代码如下:

#include <iostream>
#include <iomanip>
#include <string>
#include <vector>
#include <regex>
#include <cfloat>
using namespace std;

#define isSgn(s) (s=="+"?1:(s=="-"?2:(s=="*"?3:(s=="/"?4:(s=="^"?5:(s==""?1:0))))))

int Error=0;
int regexSplit(string&, const string, vector<string>&, int=0);
long double str2ld(string);
string ld2str(long double);
long double expSum(string);
void outError(void);

template<typename Type>
void v_erase(vector<Type> &v, size_t pos)
{	//删除容器中指定第pos和pos+1个元素 
	if (pos+1>v.size()) return;
	vector<Type> tmp(v.begin()+pos+1,v.end());
	while (v.size()>pos-1) v.pop_back();
	for (auto t:tmp) v.push_back(t);
	vector<Type>().swap(tmp); //清空临时容器tmp
}

int main(void)
{
 	long double sum;
	string str = "-1+2*3+4/5-5.8e+1+1.9999999999999999984279*2^16383";

	sum=expSum(str);

	if (Error==0)
		cout<<str<<"="<<endl<<setprecision(18)<<sum<<endl;
	else
		outError();

	cout<<"long doube最大值DBL_MAX:"<<endl<<setprecision(18)<<LDBL_MAX;

	return 0;
 }

long double expSum(string str)
{
	long double sum=0; 
	string reg("\\d+|\\d+\\.\\d+|(\\d+|\\d+\\.\\d+)e[+-]\\d+");
	
 	vector <string> v,vt;	
	regexSplit(str,reg,v);
	regexSplit(str,reg,vt,-1);

	if (!(isdigit(str[0])||str[0]=='+'||str[0]=='-')) Error=1; //表达式错 

	for(auto t:vt) if (t.size()>1||!isSgn(t)) Error=2; //表达式错

	for (int i=0;i<vt.size();i++) //vt间隔插入v
		v.insert(v.begin()+i*2,1,vt.at(i));

	for (int i=1;i<v.size();i++)
		if(v.at(i)=="^"){ //幂指运算 
				v.at(i-1)=ld2str(pow(str2ld(v.at(i-1)),str2ld(v.at(i+1))));
				if (v.at(i-1)=="inf") Error=3; //溢出,结果超出long double范围 
				if (v.at(i-1)=="nan") Error=4; //无意义,如负数的小数次方
				if (v.at(i-1)=="inf"||v.at(i-1)=="nan") break;
				v_erase(v,i+1);
				i--; //位置前移1,解决如2^2^3这样的连续幂指运算 
			}

	for (int i=1;i<v.size();i++) //把所有x/n转换成x*(1/n) 
		if(v.at(i)=="/")
			if (v.at(i+1)!="0"){
				v.at(i+1)=ld2str(1/str2ld(v.at(i+1)));
				v.at(i)="*";
			}
			else{
				Error=5; //除0错 
				break;
			}

	for (int i=1;i<v.size();i++)
		if(v.at(i)=="*"){
				v.at(i-1)=ld2str(str2ld(v.at(i-1))*str2ld(v.at(i+1)));
				v_erase(v,i+1);
				i--; //位置前移1,解决连乘运算 
			}

	for (int i=0;i<v.size();i++){
		if(v.at(i)=="-"){  // 把所有x-n转换成x+(-n) 
			v.at(i)="+";
			v.at(i+1)="-"+v.at(i+1);
		}
	}

	for (int i=1;i<v.size();i=i+2) //求出算式的代数和 
		sum+=str2ld(v.at(i));

	vector<string>().swap(v); //清空容器 
	vector<string>().swap(vt);

	return sum;
}

int regexSplit(string &str,const string str_reg,vector<string>&vect,int pos)
{
	if (pos!=-1) pos=0;  //pos=0 匹配到的位置,pos=-1匹配位置的前一字串 
	regex myPattern(str_reg); 
    sregex_token_iterator it(str.begin(),str.end(),myPattern,pos); 
    sregex_token_iterator end;
    for(;it!=end;++it) vect.push_back(*it); 
    return vect.size();  //if 0 没有匹配到,else 匹配到的个数
 } 
 
string ld2str(long double d)
{
	string s;
	stringstream ss;
	ss<<setprecision(18)<<d;
	s=ss.str();
	ss.clear();
	return s;
}
 
long double str2ld(string s)
{
	long double d;
	stringstream ss;
	ss<<s;
	ss>>setprecision(18)>>d;
	ss.clear();
	return d;
}

void outError(void)
{
	switch(Error){
		case 1:
		case 2:
			cout<<"表达式错!"; 
			break;
		case 3:
			cout<<"#INF错误!"; 
			break;
		case 4:
			cout<<"#NAN错误!"; 
			break;
		case 5:
			cout<<"#DIV/0错!"; 			
			break;
	}
	Error=0;
}

运行结果:

-1+2*3+4/5-5.8e+1+1.9999999999999999984279*2^16383=
1.18973149535723177e+4932
long doube最大值DBL_MAX:
1.18973149535723177e+4932
--------------------------------
Process exited after 0.5967 seconds with return value 0
请按任意键继续. . .

顺带找到一个比较接近long double最大值的数:1.9999999999999999984279*2^16383

看来我的系统上long double型是12字节的,最大值应该是2^16384 -1

通常double型数字到 2^1024 就溢出了