一、项目思路
该项目用于计算机软件课设,项目主要解决的部分有:中缀转后缀的处理,异常输入情况如连续输入小数点等等。项目开发中使用的工具是QT。对于中缀转后缀的基本处理思路,我采用双栈法,具体思路见我的这篇文章:利用双栈法实现简易计算器。对于异常输入的这一部分,较为简单,略过。
二、源代码
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <iostream>
#include <QMainWindow>
#include <QDebug>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void btnnumClicked(); //数字按键点击
void btnoprClicked(); //运算符符按键点击
void on_btndel_clicked();
void btnequClicked(); //等于号点击
//bool IsPop(char a, char b);
void InfixToSuffix(char* src, char* des);//中缀表达式变为后缀表达式
QString calculate(QString s2);
void btnpointClicked();
void on_btnAC_clicked();
void btnbrkClicked();
void btnfxClicked();
private:
Ui::MainWindow *ui;
bool operatorFlag;
bool waittingForOperand;
bool frontzero; //前置零 0000.01
bool pointflag;
int brkflag;
bool fxflag;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include<QStack>
#include<QPushButton>
#include<QMessageBox>
#include<map>
#include<cmath>
#include<QClipboard>
#include<QApplication>
#include<QDebug>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->lineEdit->setAlignment(Qt::AlignRight);
ui->lineEdit->setText("0");
this->setWindowTitle("简易科学计算器");
//参数初始化
operatorFlag = false;
//waittingForOperand=false;
frontzero=true;
pointflag=false;
brkflag=0;
fxflag=false;
//按键的绑定
connect(ui->btn0, SIGNAL(clicked()), this, SLOT(btnnumClicked()));
connect(ui->btn1, SIGNAL(clicked()), this, SLOT(btnnumClicked()));
connect(ui->btn2, SIGNAL(clicked()), this, SLOT(btnnumClicked()));
connect(ui->btn3, SIGNAL(clicked()), this, SLOT(btnnumClicked()));
connect(ui->btn4, SIGNAL(clicked()), this, SLOT(btnnumClicked()));
connect(ui->btn5, SIGNAL(clicked()), this, SLOT(btnnumClicked()));
connect(ui->btn6, SIGNAL(clicked()), this, SLOT(btnnumClicked()));
connect(ui->btn7, SIGNAL(clicked()), this, SLOT(btnnumClicked()));
connect(ui->btn8, SIGNAL(clicked()), this, SLOT(btnnumClicked()));
connect(ui->btn9, SIGNAL(clicked()), this, SLOT(btnnumClicked()));
//符号(加减乘除和括号 以及^
connect(ui->btnadd, SIGNAL(clicked()), this, SLOT(btnoprClicked()));
connect(ui->btndiv, SIGNAL(clicked()), this, SLOT(btnoprClicked()));
connect(ui->btnminus, SIGNAL(clicked()), this, SLOT(btnoprClicked()));
connect(ui->btnmul, SIGNAL(clicked()), this, SLOT(btnoprClicked()));
connect(ui->btnplus, SIGNAL(clicked()), this, SLOT(btnoprClicked()));
//等号
connect(ui->btnequ, SIGNAL(clicked()), this, SLOT(btnequClicked()));
//小数点
connect(ui->btnpoint,SIGNAL(clicked()), this, SLOT(btnpointClicked()));
//括号
connect(ui->btnbrk_left,SIGNAL(clicked()), this, SLOT(btnbrkClicked()));
connect(ui->btnbrk_right,SIGNAL(clicked()), this, SLOT(btnbrkClicked()));
//函数(单目
connect(ui->btnsin,SIGNAL(clicked()), this, SLOT(btnfxClicked()));
connect(ui->btncos,SIGNAL(clicked()), this, SLOT(btnfxClicked()));
connect(ui->btntan,SIGNAL(clicked()), this, SLOT(btnfxClicked()));
connect(ui->btnexp,SIGNAL(clicked()), this, SLOT(btnfxClicked()));
connect(ui->btnsqrt,SIGNAL(clicked()), this, SLOT(btnfxClicked()));
connect(ui->btnln,SIGNAL(clicked()), this, SLOT(btnfxClicked()));
connect(ui->btnlg,SIGNAL(clicked()), this, SLOT(btnfxClicked()));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::btnnumClicked()//数字键显示和储存
{
QPushButton *buttonClicked = qobject_cast<QPushButton *>(sender());
int buttonValue = buttonClicked->text().toInt(); //字符串转为整形
// if(waittingForOperand)
// {
// ui->lineEdit->clear();
// waittingForOperand = false;
// }
QString temp=ui->lineEdit->text(); //获得已输入的文本
if(temp.right(1)==")")return; //已输入的文本最右端一个字符即最后输入的字符
if(frontzero) //如果有前置已有0 防止0000.1
{
//移除最后输入0
if(temp.right(1)=='0')temp.remove(temp.length()-1,1);
//
if(buttonValue){
frontzero = false;
}
}
ui->lineEdit->setText(temp+QString::number(buttonValue));
operatorFlag=false;
}
void MainWindow::btnpointClicked(){
QString temp=ui->lineEdit->text();
if(!pointflag&&'0'<=temp.right(1)&&
temp.right(1)<='9'){
ui->lineEdit->setText(ui->lineEdit->text()+".");
pointflag=true;
frontzero=false;
operatorFlag=true;
fxflag=true;
return;
}
}
void MainWindow::btnoprClicked()
{
QPushButton *buttonClicked = qobject_cast<QPushButton *>(sender());
if(!operatorFlag){
QString op=buttonClicked->text();
ui->lineEdit->setText(ui->lineEdit->text()+op);
//waittingForOperand=true;
fxflag=false;
operatorFlag=true;
frontzero=true;
pointflag=false;//加了运算符关于数字的参数都要初始化
}
else return;
}
//三角函数调用
void MainWindow::btnfxClicked(){
if(!fxflag){
QPushButton *buttonClicked = qobject_cast<QPushButton *>(sender());
QString tfx=buttonClicked->text();
QString tle=ui->lineEdit->text();
if(tle=='0'||tle.right(2)=="(0"){
if(tle=='0')
ui->lineEdit->setText(tfx+"("+'0');
if(tle.right(2)=="(0")
{
tle.chop(1);
ui->lineEdit->setText(tle+tfx+"("+'0');
}
brkflag++;
operatorFlag=false;
frontzero=true;
pointflag=false;
//fxflag=true;
return;}
if(('0'<=tle.right(1)&&'9'>=tle.right(1))||tle.right(1)=='.')return;
ui->lineEdit->setText(tle+tfx+"("+'0');
brkflag++;
//waittingForOperand=true;
operatorFlag=false;
frontzero=true;
pointflag=false;
//fxflag=true;
return;
}
else return;
}
void MainWindow::btnbrkClicked(){
QPushButton *buttonClicked = qobject_cast<QPushButton *>(sender());
//左括号
if(buttonClicked->text()=='('){
QString temp=ui->lineEdit->text();
if( temp.right(1)!='.'&&temp.right(1)!=')'&&
(temp.right(1)>'9' ||temp.right(1)<'0'))
{
ui->lineEdit->setText(temp+"(");
brkflag++;
frontzero=false;
fxflag=false;
}
else if(temp=="0"){ui->lineEdit->setText("(");brkflag++;}
}
//右括号
if(buttonClicked->text()==")"){
if(brkflag<=0)return;
else{
QString temp_=ui->lineEdit->text();
if(temp_.right(1)>='0'&&temp_.right(1)<='9'||temp_.right(1)==")")
{
ui->lineEdit->setText(temp_+")");
brkflag--;
}
}
}
}
void MainWindow::on_btndel_clicked()
{
QString text = ui->lineEdit->text();
operatorFlag=false;
if(text.right(1)=='.'){pointflag=false;frontzero=true;}
if(text.right(1)=='(')brkflag--;
if(text.right(1)==')')brkflag++;
if(text.right(1)>='a'&&text.right(1)<='z'){
fxflag=false;
QString temp= text.right(2);
if( temp=="os"||temp=="in"||temp=="an"||temp=="xp")
text.chop(3);
if( temp=="rt")
text.chop(4);
if (temp=="lg"||temp=="ln")
text.chop(2);
}
else text.chop(1);
if(text.isEmpty())
{
text = "0";
frontzero= true;
}
QString tar=text.right(1);
if(tar<"0"||tar>"9"){
if(tar>='a'&&tar<='z'){
// text.append("(0");
// brkflag++;
operatorFlag=false;
frontzero=true;
pointflag=false;
fxflag=true;
}
else {
pointflag=operatorFlag=true;
if(tar!='.')fxflag=false;
else fxflag=true;
}
}
ui->lineEdit->setText(text);
}
void MainWindow::btnequClicked(){
if(brkflag){
QMessageBox::information(this, tr("ERROR!"),
tr("括号不匹配!"));
return;
}
QString s2 = ui->lineEdit->text();
if((s2.right(1)<'0'||s2.right(1)>'9')&&s2.right(1)!=')'){
QMessageBox::information(this, tr("ERROR!"),
tr("缺少运算分量!"));
return;
}
QString result=calculate(s2);
ui->lineEdit->setText(result);
//等于号之后的状态等于输入了一个数字
//(但没输入运算符 的状态,关于运算符的参数要重置
operatorFlag=false;
if(result=='0'){frontzero=true;
pointflag=false;
brkflag=0;
fxflag=false;
}
else frontzero=false;
}
void MainWindow::on_btnAC_clicked()
{
ui->lineEdit->setText("0");
operatorFlag = false;
// waittingForOperand=false;
frontzero=true;
pointflag=false;
brkflag=0;
fxflag=false;
}
/* 中缀转后缀 src带入中缀式,des带出后缀式 */
void MainWindow::InfixToSuffix(char* src, char* des)
{
std::map<char, int> priority;
priority['+'] = 0;
priority['-'] = 0;
priority['*'] = 1;
priority['/'] = 1;
priority['^'] = 2;
priority['s'] = 3;
priority['c'] = 3;
priority['t'] = 3;
priority['n'] = 3;//ln
priority['g'] = 3; //lg
priority['e'] = 3;//exp
priority['q'] = 3; //sqrt
des[0]='0';
des[1]=' ';
int k = 2;
/* 符号栈 */
QStack<char> symbol;
int i = 0;
while (src[i] != '\0')
{
if (src[i] == ' ') //如果当前字符是空格,则往后走一个
{
i++;
continue;
}
else if (src[i] >= '0' && src[i] <= '9' || src[i] == '.')
{
des[k++] = src[i];
if ((src[i + 1] < '0' || src[i + 1]>'9') && src[i + 1] != '.') //数字后加空格
{
des[k++] = ' ';
}
}
else if (src[i] == '(') //左括号直接入栈
{
symbol.push(src[i]);
}
else if (src[i] == ')') //遇到 ) ,输出( )之间的所有符号
{
while (symbol.top() != '(')
{
des[k++] = symbol.top();
des[k++] = ' ';
symbol.pop();
}
symbol.pop();
}
/*----------------- 运算符 -------------------------------*/
else
{
switch (src[i])
{
case '+':
case '-':
case '*':
case '/':
case '^':
if (symbol.empty()) //如果符号栈为空,直接入符号
{
symbol.push(src[i]);
}
else //否则,判断是否选择入符号栈还是出栈顶元素
{
char tempTop = symbol.top();
if (tempTop != '(' && priority[src[i]] <= priority[tempTop]) //出符号栈
{
des[k++] = symbol.top();
des[k++] = ' ';
symbol.pop();
continue;//这是作用于外部循环的continue,这样i就不会加,当前的数跳到else的push操作,再i++
}
else //当前符号优先级高,入符号栈
{
symbol.push(src[i]);
}
}
break;
case's':// sin&sqrt
if (symbol.empty()) //如果符号栈为空,直接入符号
{
if(src[i+1]=='i')
symbol.push(src[i]);
else
symbol.push(src[i+1]);
}
else //否则,判断是否选择入符号栈还是出栈顶元素
{
char tempTop = symbol.top();
if(src[i+1]=='i'){
if (tempTop != '(' && priority[src[i]] <= priority[tempTop]) //出符号栈
{
des[k++] = symbol.top();
des[k++] = ' ';
symbol.pop();
continue;
}
else //当前符号优先级高,入符号栈
{
symbol.push(src[i]);
}
}
else {
if (tempTop != '(' && priority[src[i+1]] <= priority[tempTop]) //出符号栈
{
des[k++] = symbol.top();
des[k++] = ' ';
symbol.pop();
continue;
}
else //当前符号优先级高,入符号栈
{
symbol.push(src[i+1]);
}
}
}
if (src[i + 1] == 'i')i += 2;
else i += 3;
break;
case'c':
case't':
case'e':
if (symbol.empty()) //如果符号栈为空,直接入符号
{
symbol.push(src[i]);
}
else //否则,判断是否选择入符号栈还是出栈顶元素
{
char tempTop = symbol.top();
if (tempTop != '(' && priority[src[i]] <= priority[tempTop]) //出符号栈
{
des[k++] = symbol.top();
des[k++] = ' ';
symbol.pop();
continue;
}
else //当前符号优先级高,入符号栈
{
symbol.push(src[i]);
}
}
i+=2;
break;
case'l':
if (symbol.empty()) //如果符号栈为空,直接入符号
{
symbol.push(src[i+1]);
}
else //否则,判断是否选择入符号栈还是出栈顶元素
{
char tempTop = symbol.top();
if (tempTop != '(' && priority[src[i+1]] <= priority[tempTop]) //出符号栈
{
des[k++] = symbol.top();
des[k++] = ' ';
symbol.pop();
continue;
}
else //当前符号优先级高,入符号栈
{
symbol.push(src[i+1]);
}
}
i += 1;
break;
default:
i++;
break;
}
}
i++;
/* 遍历下一字符 */
}
/*字符串已遍历完,把符号栈中剩余的符号入栈到数字栈中 */
while (!symbol.empty())
{
des[k++] = symbol.top();
des[k++] = ' ';
symbol.pop();
}
des[k] = '\0';
}
QString MainWindow:: calculate(QString s2) {
QByteArray ba2;
ba2.append(s2); //也可以 ba2 = s2.toLatin1();
char *src = ba2.data();
char* des = new char[(strlen(src)+1)*3+3];
InfixToSuffix(src, des);
QStack<double>num;
int i = 0;
while (des[i] != '\0') {
while (des[i] >= '0' && des[i] <= '9' || des[i] == ' ' || des[i] == '.') {
if (des[i] != ' ')
{
QStack<double>temp;
double tempnum = 0;
int n = 1;
double m = 0.1;
while (des[i] != ' ' && des[i] != '.') {
temp.push((des[i] - '0'));
i++;
}
if (des[i] == '.') {
while (des[++i] != ' ') {
double dt = des[i] - '0';
tempnum += dt * m;
m /= 10;
}
}
while (!temp.empty()) {
tempnum += temp.top() * n;
n *= 10;
temp.pop();
}
num.push(tempnum);
}
else i++;
}
while (des[i] < '0' || des[i]> '9' || des[i] == ' ')
{
double temp1 = 0, temp2 = 0, temp3 = 0;
switch (des[i++]) {
case'+':temp1 = num.top();
num.pop();
temp2 = num.top();
num.pop();
temp3 = temp1 + temp2;
num.push(temp3);
break;
case'-': temp1 = num.top();
num.pop();
temp2 = num.top();
num.pop();
temp3 = temp2 - temp1;
num.push(temp3);
break;
case'*':temp1 = num.top();
num.pop();
temp2 = num.top();
num.pop();
temp3 = temp2 * temp1;
num.push(temp3);
break;
case'/': temp1 = num.top();
num.pop();
temp2 = num.top();
num.pop();
temp3 = temp2 / temp1;
num.push(temp3);
break;
case'^':temp1 = num.top();
num.pop();
temp2 = num.top();
num.pop();
temp3 =std::pow(temp2,temp1);
num.push(temp3);
break;
case's':temp3 = num.top();
num.pop();
temp3 = std::sin(temp3 / 180.0 * M_PI);
num.push(temp3);
break;
case'c':temp3 = num.top();
num.pop();
temp3 =std::cos(temp3 / 180.0 * M_PI);
num.push(temp3);
break;
case't':temp3 = num.top();
num.pop();
temp3 =std::tan(temp3 / 180.0 * M_PI);
num.push(temp3);
break;
case'g':temp3 = num.top();
if(temp3<=0){
QMessageBox::information(this, tr("ERROR!"),
tr("对数计算分量不能<=0!"));
return s2;
}
num.pop();
temp3 =std::log10(temp3);
num.push(temp3);
break;
case'n':temp3 = num.top();
if(temp3<=0){
QMessageBox::information(this, tr("ERROR!"),
tr("对数计算分量不能<=0!"));
return s2;
}
num.pop();
temp3 =std::log(temp3);
num.push(temp3);
break;
case'e':temp3 = num.top();
num.pop();
temp3 =std::exp(temp3);
num.push(temp3);
break;
case'q':
temp3 = num.top();
num.pop();
temp3 =std::sqrt(temp3);
num.push(temp3);
break;
default:break;
}
if (des[i] == '\0')break;
}
}
return QString::number(num.top());
}
main.c
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
三、项目总结
该项目难度较大,尤其是中缀表达式转后缀表达式难以处理,同时要考虑的输入情况较多,我有些情况当时没有考虑到,如对于负数的乘除处理有瑕疵。