界面展示
1.用戶界面類設計
需要使用QWidget組件作為頂層窗口,QLineEdit組件作為輸入框,QPsuhButton作為按鈕
1.1 在代碼里處理按鍵消息時,需要處理下用戶輸入的格式(方便邏輯模塊計算)
1)匹配括號成對出現,左括號必然先於右括號出現
- 當有左括號出現時,則status++
- 當有右括號出現時,並且status!=0時,則右括號有效,並status--
2)判斷每個按鍵是否合法
數字前面不能為:右括號
比如:
10+3)5*2 //出錯,數字5前面不能為右括號
小數點前面不能為空,只能是數字,並且一串數字只能有一個小數點
比如:
1.23.45 //出錯,一串數字只能有一個小數點
加減號前面不能為:小數點,並且前面不能連續有兩次加減乘除,或者是(和運算符
比如:
7*-+10 //出錯,+號前面出現兩次加減乘除 7. + //出錯,+號前面不能有小數點 7-(--5) //出錯, -5數字前面有個減號
乘除號前面不能為:左括號,空,小數點,加減乘除,
比如:
*1+(/5+10) //出錯, *前面不能為空,且除法前面不能為左括號
左括號前面不能為:右括號,數字,小數點,並且前面不能連續有兩次加減乘除
比如:
( )+10(11+10) //出錯,( 前面不能為數字
右括號前面不能為:空,加減乘除,小數點,左括號,並且與左括號成對出現
比如:
) + (10+ 5.) //出錯,右括號不能出現在開頭,並且右括號前面不能有小數點
2.邏輯模塊類設計
如何計算四則運算表達式.比如:
2.1 將中綴表達式進行數字和運算符的分離,並保存到隊列里
1)需要考慮 + - 是正負號,還是加減運算符
當+-出現在表達式開頭時,表示為正負號,比如:
+7-5; //+出現在開頭,說明這個+,表示的是正號,而不是加號
當出現+-時,並且前面還有運算符時,表示為正負號,比如:
7*-5; //-前面還有*,說明這個-,表示的是負號,而不是減號
當出現+-時,並且前面還有左括號時,表示為正負號,比如:
9+(-3+4) //-前面還有(,說明這個-,表示負號,而不是減號
2)以下圖的中綴表達式為例
分離后,隊列的每個元素應該為:
str[0] = "+9" str[1] = "+" str[2] = "(" str[3] = "-3" str[4] = "-" str[5] = "-1" str[6] = ")" str[7] = "*" str[8] = "-5"
2.2 將分解出來的中綴表達式隊列 轉換為后綴表達式隊列
比如+9 + (-3 - -1)* -5,轉換為后綴表達式為:
+9, -3, -1, -, -5, *, +
后綴表達式隊列的每個元素應該為:
str[0] = "+9" str[1] = "-3" str[2] = "-1" str[3] = "-" str[4] = "-5" str[5] = "*" str[6] = "+"
思路
由於運算符處於后綴,所以需要使用棧,用來存儲運算符以及括號
轉換過程
-當隊列元素為數字時
- 直接保存到隊列
-當隊列元素為加減時
- 判斷棧頂的運算優先級,由於+-的優先級小於等於所有運算符
- 所以循環取出棧頂的運算符並入隊列
- 直到遇到棧為空、遇到左括號時才停止,最后再將當前+-入棧
-當隊列元素為乘除時
- 判斷棧頂的運算優先級,由於*/的優先級只小於等於*/
- 所以循環判斷棧頂運算符,如果棧頂運算符是*/,則取出並入棧
- 直到遇到棧為空、遇到左括號、遇到+-時才停止,最后再將當前*/入棧
-當前隊列元素為左括號時
- 直接入棧
-當前隊列元素為右括號時
- 循環將棧頂運算符出棧並入隊列
- 直到遇到左括號停止,並將左括號出棧棄掉.
-當隊列元素判斷結束后
- 判斷棧是否為空,如果不為空,則將棧存儲的運算符出棧並入隊列
示意圖如下所示
2.3 將后綴表達式的值計算出來
通過逆波蘭表達式計算,思路如下
遇到數字時
- 入棧
遇到運算符時
- 依次取出右、左操作數,然后進行計算(有除法時,需要判斷除數是否為0)
- 計算完成后,再將結果入棧
當后綴表達式隊列對空時
- 表示遍歷結束,此時棧中若只剩下唯一數字,則算出了結果答案.
示意圖如下所示
3.代碼實現
3.1 與界面相關的模塊,用QCalculatorUI類實現
QCalculatorUI.h代碼如下:
#ifndef QCALCULATORUI_H #define QCALCULATORUI_H #include <QWidget> #include <QLineEdit> #include <QPushButton> #include <QDebug> #include <QString> #include "QCalculatorDec.h" class QCalculatorUI : public QWidget { Q_OBJECT private: QCalculatorDec mDec; QLineEdit *mline; //顯示行 QPushButton *mbuton[20]; //按鈕成員 QCalculatorUI(); bool construct(); private slots: void handler_clicked(); //處理按鍵消息 public: int MatchingBoth(QString &str1,const char *str2); //匹配str1和str2,判斷str1是否有str2的字符 int LastMatchingBoth(QString &str1,const char *str2); //反向匹配str1和str2 static QCalculatorUI* NewIntance(); //成員需要資源申請,所以使用二階構造 void show(); }; #endif // QCALCULATORUI_H
QCalculatorUI.cpp代碼如下:
#include "QCalculatorUI.h" QCalculatorUI::QCalculatorUI() : QWidget(NULL,Qt::WindowCloseButtonHint) { } bool QCalculatorUI::construct() { int ret; const char* butnText[20]= { "<-","CE", "7","8","9","+","(", "4","5","6","-",")", "1","2","3","*","=", "0", ".","/", }; const int butnPos[20][4]= //存放 x y w h { {10,50,90,40},{110,50,140,40}, //<- CE {10,100,40,40},{60,100,40,40},{110,100,40,40},{160,100,40,40},{210,100,40,40}, //7 8 9 + ( {10,150,40,40},{60,150,40,40},{110,150,40,40},{160,150,40,40},{210,150,40,40}, //4 5 6 - ) {10,200,40,40},{60,200,40,40},{110,200,40,40},{160,200,40,40},{210,200,40,90}, //1 2 3 * = {10,250,90,40}, {110,250,40,40},{160,250,40,40}, //0 . / }; mline =new QLineEdit(this); if(mline==NULL) return false; mline->resize(240,30); mline->move(10,10); mline->setAlignment(Qt::AlignRight); mline->setReadOnly(1); // mline->setFont(QFont(0,10)); //設置字體 this->setWindowTitle("計算器"); for(int i=0;i<20;i++) { mbuton[i]= new QPushButton(butnText[i],this); if(mbuton[i]==NULL) return false; mbuton[i]->resize(butnPos[i][2],butnPos[i][3]); mbuton[i]->move(butnPos[i][0],butnPos[i][1]); QObject::connect(mbuton[i],SIGNAL(clicked()),this,SLOT(handler_clicked())); } return true; } QCalculatorUI* QCalculatorUI::NewIntance() //二階構造 { QCalculatorUI* ret = new QCalculatorUI(); if(ret==NULL || !ret->construct()) { delete ret; return NULL; } return ret; } int QCalculatorUI::LastMatchingBoth(QString& str1,const char* str2) //反向匹配str1和str2 { for(int i=str1.length();i>=0;i--) { for(unsigned int j=0;j<strlen(str2);j++) if(str1[i]==str2[j]) return i; } return -1; } int QCalculatorUI::MatchingBoth(QString& str1,const char* str2) //匹配str1和str2,判斷str1是否有str2的字符 { for(int i=0;i<str1.length();i++) { for(unsigned int j=0;j<strlen(str2);j++) if(str1[i]==str2[j]) return i; } return -1; } void QCalculatorUI::handler_clicked() //處理按鍵消息 { static int ClearLine=0; static int bracket_cnt=0; //圓括號計數 QPushButton *btn =dynamic_cast<QPushButton* >(sender()); //獲取對象 QString line = mline->text(); QString text = btn->text(); //獲取消息 if(ClearLine) { mline->setText(""); line.clear(); ClearLine=0; }if(text>="0"&&text<="9") //數字 { QString tmp= line.right(1); if(tmp.length() && tmp[0]==')') //數字前面不能為右括號 { return; } line+=text; } else if(text=="." ) //小數點 { QString tmp= line.right(1); if(tmp.length()) //小數點前面只能是數字 { if(MatchingBoth(tmp,"0123456789")== -1) //沒找到數字 { return; } } else //小數點前面為空 { return ; } int pos= LastMatchingBoth(line,"+-*/.()"); //反向查找 if(pos!= -1 &&line[pos]=='.' ) //一串數字只能有一個小數點 { return ; } line+=text; } else if(text=="+"||text=="-") //加減號 { QString tmp= line.right(1); if(tmp.length()&& tmp[0]=='.') //前面不能為:小數點 { return ; } tmp= line.right(2); if(tmp.length()==2) //前面不能連續有兩次加減乘除 { if(tmp[0]=='+'||tmp[0]=='-'||tmp[0]=='*'||tmp[0]=='/'||tmp[0]=='(') if(tmp[1]=='+'||tmp[1]=='-'||tmp[1]=='*'||tmp[1]=='/') return ; } line+=text; } else if(text=="*"||text=="/") //乘除號 { QString tmp= line.right(1); if(tmp.length()) //前面不能為:左括號,小數點,加減乘除, { if(MatchingBoth(tmp,"(.+-*/")!= -1) //查找左括號,小數點,加減乘除 { return; } } else //乘除號前面不能為空 return; line+=text; } else if(text=="(") //左括號 { QString tmp= line.right(1); if(tmp.length()) //前面不能為:右括號,數字,小數點 { if(MatchingBoth(tmp,")0123456789.")!= -1) //查找右括號,數字,小數點 { return; } } tmp= line.right(2); if(tmp.length()==2) //前面不能連續有兩次加減乘除 { if(tmp[0]=='+'||tmp[0]=='-'||tmp[0]=='*'||tmp[0]=='/') if(tmp[1]=='+'||tmp[1]=='-'||tmp[1]=='*'||tmp[1]=='/') return ; } line+=text; bracket_cnt++; } else if(text==")") //右括號 { QString tmp= line.right(1); if(bracket_cnt==0) //前面沒有左括號 return; if(tmp.length()) //前面不能為:加減乘除,小數點,左括號 { if(MatchingBoth(tmp,"+-*/.(")!= -1) //查找加減乘除,小數點,左括號 { return; } } else //右括號前面不能為空 return; line+=text; bracket_cnt--; } else if(text=="<-") //<- { if(line.length()) line.chop(1); } else if(text=="CE") //清空 { line.clear(); bracket_cnt=0; } else if(text=="="&& line.length()) { QString ret=mDec.Result(line); if(ret==NULL) //除數為0 { line += " : "; line +="除數不能為0"; } else if(ret=="Error") { line += ":"; line +="格式出錯"; } else { line += "="; line += ret; } ClearLine =1; } mline->setText(line); } void QCalculatorUI::show() //顯示窗口 { QWidget::show(); this->setFixedSize(this->width(),this->height()); }
3.2 與邏輯相關的用QCalculatorDec類實現
QCalculatorDec.h代碼如下:
#ifndef QCALCULATORDEC_H #define QCALCULATORDEC_H #include <QString> #include <QStack> #include <QQueue> #include <QDebug>
class QCalculatorDec { private: QQueue<QString> Split(const QString& exp); //分離前綴 QQueue<QString> Transfer(QQueue<QString>& exp); //將中綴隊列轉換為后綴隊列 QString Calculate(QQueue<QString>& exp); //將后綴隊列計算出結果 QString Calculate(QString& l,QString& op,QString& r ); QString ValidNum(QString str); public: QCalculatorDec(); QString Result(const QString& exp); }; #endif // QCALCULATORDEC_H
QCalculatorDec.cpp代碼如下:
#include "QCalculatorDec.h" QCalculatorDec::QCalculatorDec() { } QQueue<QString> QCalculatorDec::Split(const QString& exp) //分離前綴 { QQueue<QString> ret; QString num=""; for(int i=0;i<exp.length();i++) { if( (exp[i]=='.') || ( (exp[i]>='0') && (exp[i]<='9') )) //判斷小數點和數字 { num += exp[i]; } else if(exp[i]== '(' || exp[i]== ')' || exp[i]== '*' || exp[i]== '/' ) { if(!num.isEmpty()) { ret.enqueue(num); //將數字入隊列 num.clear(); } ret.enqueue(exp[i]); } else if(exp[i]== '+' || exp[i]== '-') // + - 需要特殊處理 { if(i==0) //表達式開頭,說明是正負號 { num+= exp[i]; } else if(exp[i-1]=='(' || exp[i-1]=='+' || exp[i-1]=='-' || exp[i-1]=='*' || exp[i-1]=='/') { num+= exp[i]; } else //否則是加減運算符 { if(!num.isEmpty()) { ret.enqueue(num); //將數字入隊列 num.clear(); } ret.enqueue(exp[i]); } } } if(!num.isEmpty()) //遍歷完成,判斷是否還有數字 { ret.enqueue(num); num.clear(); } return ret; } QQueue<QString> QCalculatorDec::Transfer(QQueue<QString>& exp) //將中綴隊列轉換為后綴隊列 { QStack<QString> stack; QQueue<QString> ret; bool num_ok; QString symbol; while(!exp.isEmpty()) { symbol = exp.dequeue(); //出隊列 symbol.toDouble(&num_ok); if(num_ok==true) //數字 { stack.push(symbol); } else if(symbol=="+"||symbol=="-") { while(!stack.isEmpty() &&(stack.top()!="(")) { ret.enqueue(stack.pop()); //取出棧頂運算符並入隊列 } stack.push(symbol); } else if(symbol=="*"||symbol=="/") { while(!stack.isEmpty() && (stack.top()!="(") && (stack.top()!="+") && (stack.top()!="-")) { ret.enqueue(stack.pop()); //取出棧頂運算符並入隊列 } stack.push(symbol); } else if(symbol == "(") { stack.push(symbol); } else if(symbol ==")") { while(!stack.isEmpty() && (stack.top()!="(")) { ret.enqueue(stack.pop()); //取出棧頂運算符並入隊列 } if(stack.top()=="(") stack.pop(); } } while(!stack.isEmpty()&& (stack.top()!="(")) //遍歷完成,判斷棧里是否為空 { ret.enqueue(stack.pop()); //取出棧頂運算符並入隊列 }return ret; } QString QCalculatorDec::ValidNum(QString str) { QString num;
if(str.indexOf(".")== -1) //判斷是否小數 return str; while(str.length()>1) //避免0被去掉 { num=str.right(1); if(num=="."||num=="0") { str.chop(1); if(num==".") return str; } else return str; } return str; } QString QCalculatorDec::Calculate(QString& l,QString& op,QString& r ) { double left,right,res; QString ret=""; left = l.toDouble(); right = r.toDouble(); if(op == "+") { res = left + right; } else if(op == "-") { res = left - right; } else if(op == "*") { res = left * right; } else if(op == "/") { if( (right>(-0.000000000000001)) && (right<(0.000000000000001)) ) //判斷除數為0 return NULL; else res = left/right; } ret.sprintf("%f",res); return ret; } QString QCalculatorDec::Calculate(QQueue<QString>& exp) //將后綴隊列計算出結果 { QStack<QString> stack; QString symbol,L,R,op,ret; bool num_ok; while(!exp.isEmpty()) { symbol = exp.dequeue(); //出隊列 symbol.toDouble(&num_ok); if(num_ok==true) //數字 { stack.push(symbol); } else //運算符 { if(stack.size()<2) return "Error"; R= stack.pop(); L= stack.pop(); ret = Calculate(L,symbol,R ); if(ret==NULL) return ret; stack.push(ret); } } if(stack.size()==1) //遍歷完成,結果只有一個 { return ValidNum(stack.pop()); } else {return "Error"; } } QString QCalculatorDec::Result(const QString& exp) { QQueue<QString> q=Split(exp); //分離中綴 q=Transfer(q); //轉換為后綴 return Calculate(q); //返回結果 }
3.3 main.cpp代碼如下
#include <QtGui> #include "QCalculatorUI.h" #include "QCalculatorDec.h"
int main(int argc, char* argv[]) { /*設置字體為GBK*/ QTextCodec *codec = QTextCodec::codecForName("GBK"); QTextCodec::setCodecForTr(codec); QTextCodec::setCodecForLocale(codec); QTextCodec::setCodecForCStrings(codec); QApplication app(argc,argv); QCalculatorUI* ui = QCalculatorUI::NewIntance(); if(ui==NULL) return false; ui->show(); return app.exec(); }