计算机软件技术实习
第四周学习记录
实验1——支持算术表达式求解的计算器
前言
这周,我已经基本完成了实验1。接下来将为大家演示和讲解我所编写的计算器的实现过程。
计算器的GUI界面
如图,最终我所敲定的功能包括求余、加减乘除、带括号的混合实数运算。当然,能成功处理小数和负数的运算。
计算器的图标如下
加减乘除、求余、小数点、括号和数字的输入
首先是定义的全局变量
int Lbracket = 0;//可添加右括号的数量
CString str;
CString opt("+-*/%(."); //除右括号外的操作符
CString optp("+-*/()%"); //除小数点外的操作符
stack<CString> opStack;//创建操作符栈
stack<double> Res_Stack;//创建运算栈
queue<CString> postQueue;//创建后缀表达式队列
CString first("*/%"); //高优先级运算符
CString second("+-"); //低优先级运算符
加号的输入
void CCalculator01Dlg::OnBnClickedButton4()//加号
{
if (str.IsEmpty() == true)
{
return;
}
else if (opt.Find(str[str.GetLength() - 1]) != -1 )
{
return;
}
else
{
str += "+";
windows1.SetWindowTextW(str);
}
}
减号的输入
注意,这里的减号能自动实现负数的输入
void CCalculator01Dlg::OnBnClickedButton10()//减号
{
if (str.IsEmpty())//判断为负号
{
str += "(0-";
Lbracket++;
windows1.SetWindowTextW(str);
}
else if (str[str.GetLength() - 1] == '.')
{
return;
}
else if (opt.Find(str[str.GetLength() - 1]) != -1)//判断为负号
{
str += "(0-";
Lbracket++;
windows1.SetWindowTextW(str);
}
else
{
str += "-";
windows1.SetWindowTextW(str);
}
}
乘号的输入
void CCalculator01Dlg::OnBnClickedButton15()//乘号
{
if (str.IsEmpty() == true)
{
return;
}
else if (opt.Find(str[str.GetLength() - 1]) != -1)
{
return;
}
else
{
str += "*";
windows1.SetWindowTextW(str);
}
}
除号的输入
void CCalculator01Dlg::OnBnClickedButton20()//除号
{
if (str.IsEmpty() == true)
{
return;
}
else if (opt.Find(str[str.GetLength() - 1]) != -1)
{
return;
}
else
{
str += "/";
windows1.SetWindowTextW(str);
}
}
求余号的输入
void CCalculator01Dlg::OnBnClickedButton16()//求余
{
if (str.IsEmpty() == true)
{
str += "%";
windows1.SetWindowTextW(str);
}
else if (opt.Find(str[str.GetLength() - 1]) != -1)
{
return;
}
else
{
str += "%";
windows1.SetWindowTextW(str);
}
}
小数点的输入
void CCalculator01Dlg::OnBnClickedButton19()//小数点
{
bool HavePnt = false; //代表上一个运算符之前有无小数点
int i = str.GetLength() - 1;
do //对上述进行判断
{
if (str.IsEmpty())
{
break;
}
if (str[i] == '.')
{
HavePnt = true;
break;
}
i--;
} while (i >= 0 && opt.Find(str[i]) != -1);
if (str.IsEmpty())//自动补0
{
str += "0.";
windows1.SetWindowTextW(str);
}
else if (HavePnt == true)//禁止输入
{
return;
}
else if (opt.Find(str[str.GetLength() - 1]) != -1)//自动补0
{
str += "0.";
windows1.SetWindowTextW(str);
}
else if(str[str.GetLength()-1]>='0'&& str[str.GetLength() - 1] <= '9')//正常输入小数点
{
str += ".";
windows1.SetWindowTextW(str);
}
}
左括号和右括号的输入
void CCalculator01Dlg::OnBnClickedButton21()//正括号
{
Lbracket++;
str += "(";
windows1.SetWindowTextW(str);
}
void CCalculator01Dlg::OnBnClickedButton22()//反括号
{
if (Lbracket != 0)
{
if (opt.Find(str[str.GetLength() - 1]) != -1 && str[str.GetLength() - 1] != '(')
{
return;
}
Lbracket--;
str += ")";
windows1.SetWindowTextW(str);
}
}
退格和清除
void CCalculator01Dlg::OnBnClickedButton5()//退格
{
if (str.IsEmpty() == true)
{
return;
}
if (str[str.GetLength() - 1] == '(')
{
Lbracket--;
}
else if (str[str.GetLength() - 1] == ')')
{
Lbracket++;
}
str.Delete(str.GetLength() - 1);
windows1.SetWindowTextW(str);
}
void clear(stack<CString> s)//清除字符栈
{
while (!s.empty())
{
s.pop();
}
}
void clear(stack<double> s)//清除实数栈
{
while (!s.empty())
{
s.pop();
}
}
void clear(queue<CString> q)//清除队列
{
while (!q.empty())
{
q.pop();
}
}
void CCalculator01Dlg::OnBnClickedButton7()//清除
{
str.Empty();
windows1.SetWindowText(str);
Lbracket = 0;
clear(opStack);
clear(Res_Stack);
clear(postQueue);
CString s;
s.Empty();
windows2.SetWindowText(s);
数字的输入
因为十个数字输入函数基本一致,所以只以输入1来代表。
void CCalculator01Dlg::OnBnClickedButton1()//1
{
if (str.IsEmpty() == false && str[str.GetLength() - 1] == ')')
{
return;
}
str += "1";
windows1.SetWindowTextW(str);
}
等号功能的实现
包括了对中缀表达式向后缀表达式的转换以及对算式合法性的基本检验和对后对表达式的运算操作。
void CCalculator01Dlg::OnBnClickedButton12()//等号
{
if (str.IsEmpty())
{
return;
}
else if (Lbracket != 0)
{
Bracket myBkt;
myBkt.DoModal();
return;
}
else if (opt.Find(str[str.GetLength() - 1]) != -1)
{
Bracket myBkt;
myBkt.DoModal();
return;
}
ofstream outfile;
outfile.open("Memory.txt", ios::app, ios::_Noreplace);
outfile << CT2A(str)<<"=";
CString getstr = str;//获取文本框1中的字符串
int str_len = str.GetLength();//获取字符串长度
int pnt = 0;//小数点数量
for (int i = 0; i < str_len; i++)
{
if (getstr[i] == '.')
{
pnt++;
}
}
int index = 0;//当前是第几个小数点
int* front = new int[pnt];
for (int i = 0; i < pnt; i++)
{
front[i] = 0;
}
int* back = new int[pnt];
for (int i = 0; i < pnt; i++)
{
back[i] = 0;
}
if (pnt != 0)
{
//判断每个小数点整数部分长度和小数部分长度
for (int i = 0; i < str_len; i++)
{
if (getstr[i] == '.')
{
for (int f = i - 1; f >= 0; f--)//整数部分长度
{
if (opt.Find(getstr[f]) == -1)
{
front[index]++;
}
else
{
break;
}
}
for (int b = i + 1; b < str_len; b++)//小数部分长度
{
if (opt.Find(getstr[b]) == -1 && getstr[b] != ')')
{
back[index]++;
}
else
{
break;
}
}
index++;
}
}
index = 0;
}
//把中缀表达式转化成后缀表达式
for (int i = 0; i < str_len; i++)
{
if (getstr[i] >= '0' && getstr[i] <= '9')//如果是数字
{
//放入队列中
int w = 0;//实数位数
for (int j = i; j < str_len; j++)
{
if (optp.Find(getstr[j]) != -1)
{
break;
}
w++;
}
bool havepnt = false;
for (int x = i; x < str_len; x++)//判断有无小数点
{
if (optp.Find(getstr[x]) != -1)
{
break;
}
if (getstr[x] == '.')
{
havepnt = true;
break;
}
}
double num = 0;
if (havepnt)//如果有小数点
{
for (int j = i; j < i + front[index]; j++)
{
num *= 10;
num += static_cast<unsigned __int64>(getstr[j]) - static_cast <unsigned __int64>('0');
}
for (int j = i + front[index] + 1; j < i + front[index] + back[index] + 1;j++)
{
num *= 10;
num += static_cast<unsigned __int64>(getstr[j]) - static_cast <unsigned __int64>('0');
}
for (int j = 0; j < back[index]; j++)
{
num /= 10;
}
index++;
}
else//没有小数点
{
for (int j = i; j < i + w; j++)
{
num *= 10;
num += static_cast<unsigned __int64>(getstr[j]) - static_cast <unsigned __int64>('0');
}
}
CString c;
c.Format(_T("%f"), num);
postQueue.push(c);
i += w - 1;
}
else
{
if (getstr[i] == '(')//左括号
{
opStack.push((CString)getstr[i]);
}
else if (getstr[i] == ')')//右括号
{
for (; true;)
{
if (opStack.top() == '(')
{
opStack.pop();
break;
}
else
{
postQueue.push(opStack.top());
opStack.pop();
}
}
}
else//其他运算符
{
if (opStack.empty())
{
opStack.push((CString)getstr[i]);
}
else if (second.Find(getstr[i]) != -1)//低优先级
{
for (; true;)
{
if (opStack.empty() || opStack.top() == '(')
{
break;
}
postQueue.push(opStack.top());
opStack.pop();
}
opStack.push((CString)getstr[i]);
}
else//高优先级
{
if (second.Find(opStack.top()) != -1 || opStack.top() == '(')
{
opStack.push((CString)getstr[i]);
}
else
{
for (; true;)
{
postQueue.push(opStack.top());
opStack.pop();
if (opStack.empty() || second.Find(opStack.top()) != -1)
{
opStack.push((CString)getstr[i]);
break;
}
}
}
}
}
}
}
while (!opStack.empty())
{
postQueue.push(opStack.top());
opStack.pop();
}
index = 0;
while (!opStack.empty())
{
postQueue.push(opStack.top());
opStack.pop();
}
double num1, num2;
while (!postQueue.empty())
{
double NUM;
if (isDouble(postQueue.front()))//如果为数字
{
//防止算术溢出,在运算前进行长度转换
NUM = _tstof(postQueue.front());
Res_Stack.push(NUM);
postQueue.pop();
}
else//如果为操作符
{
num1 = Res_Stack.top();
Res_Stack.pop();
num2 = Res_Stack.top();
Res_Stack.pop();
if (postQueue.front() == '+')
{
Res_Stack.push(num1 + num2);
postQueue.pop();
}
else if (postQueue.front() == '-')
{
Res_Stack.push(num2 - num1);
postQueue.pop();
}
else if (postQueue.front() == '*')
{
Res_Stack.push(num1 * num2);
postQueue.pop();
}
else if (postQueue.front() == '/')
{
Res_Stack.push(num2 / num1);
postQueue.pop();
}
else if (postQueue.front() == '%')
{
int a1 = (int)num1;
int a2 = (int)num2;
if (a1 - num1 == 0 && a2 - num2 == 0)
{
Res_Stack.push(a2% a1);
postQueue.pop();
}
else
{
Bracket myBkt;
myBkt.DoModal();
CCalculator01Dlg::OnBnClickedButton7();
return;
}
}
}
}
double show_num = Res_Stack.top();
CString show;
show.Format(_T("%lf"), show_num);
bool Point = false;
for (int i = 0; i < show.GetLength() - 1; i++)
{
if (show[i] == '.')
{
Point = true;
}
}
if (Point)
{
for (int i = show.GetLength() - 1; i >= 0; i--)
{
if (show[i] > '0' && show[i] <= '9')
{
break;
}
else if (show[i] == '.')
{
show.Delete(i);
break;
}
else
{
show.Delete(i);
}
}
}
windows2.SetWindowTextW(show);
outfile << CT2A(show) << endl << endl;
outfile.close();
return;
}
记忆更新和记忆清除
目前只能实现点击记忆清除按钮后,要重启计算器后才能清除。
void CCalculator01Dlg::OnBnClickedButton23()//更新历史记录
{
CString strLine, strTemp, result;
int row = 0;
CStdioFile file(L"Memory.txt", CFile::modeRead);
while (file.ReadString(strLine))
{
//char *str = strLine.GetBufferSetLength(strLine.GetLength());
char* str = (char*)strLine.GetBufferSetLength(strLine.GetLength());
//char *p;
if (strLine != "")
{
result += _T("\r\n") + strLine + _T("\r\n");
//result += strLine;
windows3.SetWindowTextW(result);
//result = " ";
row++;
}
}
file.Close();
}
void CCalculator01Dlg::OnBnClickedButton25()//清空历史记录
{
ofstream outfile;
outfile.open("Memory.txt", ios::trunc, ios::_Noreplace);
CString s;
s.Empty();
windows3.SetWindowTextW(s);
}
中缀表达式转后缀表达式算法:
需要建立:
①后缀表达式队列:postQueue,用于存储逆波兰表达式(其实不用队列排序直接输出也行)
②操作符栈:opStack,对用户输入的操作符进行处理,用于存储运算符
算法:
从左向右依次读取算术表达式的元素X,分以下情况进行不同的处理:
(1)如果X是操作数,直接入队
(2)如果X是运算符,再分以下情况:
a)如果栈为空,直接入栈。
b)如果X==”(“,直接入栈。
c)如果X==”)“,则将栈里的元素逐个出栈,并入队到后缀表达式队列中,直到第一个配对的”(”出栈。(注:“(”和“)”都不 入队)
d)如果是其他操作符(+ - * /),则和栈顶元素进行比较优先级。 如果栈顶元素的优先级大于等于X,则出栈并把栈中弹出的元素入队,直到栈顶元素的优先级小于X或者栈为空。弹出完这些元素后,才将遇到的操作符压入到栈中。
(3)最后将栈中剩余的操作符全部入队。
后缀表达式求解:
需要用到一个结果栈Res_Stack :用于存放计算的中间过程的值和最终结果
首先准备一个栈Res_Stack.
1、从左开始向右遍历后缀表达式的元素。
2、如果取到的元素是操作数,直接入栈Res_Stack,如果是运算符,从栈中弹出2个数进行运算,然后把运算结果入栈
3、当遍历完后缀表达式时,计算结果就保存在栈里了。
结语
以上就是我第一次实验的成果,感想大家!