涉及的知識點
- fstream 中 getline()
- fgetc() 和 fseek() 深入理解
- 詞法分析程序思路
- tkinter
心路歷程
一開始無從下手,再到讀懂前輩程序,更改,完善功能,增加界面,檢查實驗,兩天不到,一氣呵成。
結果總結起來竟發現知識點的確不多,最重要的還是思路。
源碼(註釋挺詳細)
一個字節一個字節的掃描txt文件,根據第一個字符是字母數字還是運算符分割符分別判斷接下來接收的是什麼,直到遇到不符合要求的字符,結束,然後判斷類型。
#include<iostream>
#include<fstream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cstdlib>
using namespace std;
int aa;// fseek的時候用來接着的
string word="";
string reserved_word[70];//保留字
char buffer;//每次讀進來的一個字符
int num=0;//每個單詞中當前字符的位置
int line_=1; //行數
int row=1; //列數,就是每行的第幾個
bool flag; //文件是否結束了
int flag2;//單詞的類型
int reservedn=0;// 保留字個數
//設置保留字
int set_reserve()
{
ifstream fin;
fin.open("baoliuzi.txt");
int i=0;
while(!fin.eof()){//這是註釋
getline(fin,reserved_word[i]);/*註釋*/
i++;
}
fin.close();
return i-1;
}
//看這個字是不是字母
bool judge_word(char x)
{
if(x>='a' && x<='z' || x>='A' && x<='Z' ){
return true;
}
else return false;
}
//看這個字是不是數字
bool judge_number(char x)
{
if(x>='0' && x<='9'){
return true;
}
else return false;
}
//看這個字符是不是界符
bool judge_jiefu(char x)
{
if(x=='('||x==')'||x==','||x==';'||x=='{'||x=='}'||x=='['||x==']'
||x=='\''||x=='\"'||x=='.' ){
return true;
}
else return false;
}
//+ - * !
bool judge_yunsuanfu1(char x)
{
if(x=='+'||x=='-'||x=='*'||x=='!')
{
return true;
}
else return false;
}
//等於 賦值,大於小於 大於等於,小於等於,大於小於
bool judge_yunsuanfu2(char x)
{
if(x=='='|| x=='>'||x=='<'){
return true;
}
else return false;
}
// || &&
bool judge_yunsuanfu3(char x)
{
if(x=='|'|| x=='&'){
return true;
}
else return false;
}
//這個最大的函數的總體作用是從文件裏讀一個單詞
int scan(FILE *fp)//一個指向文件的指針
{
buffer=fgetc(fp);//從文件指針fp指向的文件中讀取一個字符並返回,指針自動後移
if(feof(fp)){ //文件結束:返回非0值;文件未結束:返回0值
flag=0;return 0;
}
//用line_和row給詞語定位
else if(buffer==' '||buffer=='\t')//有的人喜歡用tab
{
row++;
return 0;
}
else if(buffer=='\n'||buffer=='\r')
{
line_++;
row=1;
return 0;
}
//如果是字母開頭或'_' 看關鍵字還是普通單詞
else if(judge_word(buffer) || buffer=='_')
{
word+=buffer;row++;//字符串加上ascll碼值相當於字符串拼接
while((buffer=fgetc(fp)) && (judge_word(buffer) || judge_number(buffer) || buffer=='_'))
{
word+=buffer;row++;
}//讀完了一個單詞
if(feof(fp)){
flag=0;return 1;
}
//這個函數的意義是 因爲保留字不區分大小寫 要把大寫字母全變成小寫再比較
string temp=word;
for(int j=0;j<temp.length();j++)
{
if(temp[j]>='A' && temp[j]<='Z')
{
temp[j]+=32;
}
}
for(int i=0;i<reservedn;i++){//擴展保留字
if(temp==reserved_word[i]){ //由於取到空串後指針再往後移,所以回退一字節
aa=fseek(fp,-1,SEEK_CUR);//將指針從當前位置後退一字節(一個字符),成功返回0
//cout<<fgetc(fp)<<endl;
return 3;
}
}
aa=fseek(fp,-1,SEEK_CUR);//如果之前return,此句不執行
return 1;
}
//開始是加減乘 一定是類型4
else if(judge_yunsuanfu1(buffer))
{
//接收到+ - * !後
word+=buffer;row++;
word+=fgetc(fp);row++;//再接收一個看是不是等於號
if(word=="+="||word=="*="||word=="-="||word=="!="||word=="--"||word=="++"){
return 4;
}
else{ //如果不是等於號,至少它還是個運算符,記得指針回退一下
aa=fseek(fp,-1,SEEK_CUR);
row--;
return 4;
}
}
// 判斷 && ||
else if(judge_yunsuanfu3(buffer))
{
//接收到 & |後
word+=buffer;row++;
word+=fgetc(fp);row++;//再接收一個看
if(word=="||"||word=="&&"){
return 4;
}
else{ //如果不是
aa=fseek(fp,-1,SEEK_CUR);
row--;
return 4;
}
}
//開始是數字就一定是數字
else if(judge_number(buffer))
{
word+=buffer;row++;
while((buffer=fgetc(fp)) && judge_number(buffer))
{
word+=buffer;row++;
}
if(feof(fp)){
flag=0;return 2;
}
aa=fseek(fp,-1,SEEK_CUR);
return 2;
}
//檢驗界符
else if(judge_jiefu(buffer))
{
word+=buffer;row++;
return 6;
}
//檢驗 <=、 >=、 <>、 == =、 <、>
else if(judge_yunsuanfu2(buffer))
{
row++;
word+=buffer;
if(buffer=='<') //爲了檢驗<> <=
{
buffer=fgetc(fp);
if(buffer=='>' || buffer=='=')
{
word+=buffer;
row++;
return 5;
}
}
//檢驗 >= ==
else{
buffer=fgetc(fp);
if(buffer=='=')
{
word+=buffer;
row++;
return 5;
}
}
if(feof(fp)){
flag=0;
}
//如果再接收一個字符並不是> = <,說明是單運算符返回4,指針要回退
aa=fseek(fp,-1,SEEK_CUR);
return 4;
}
//首字符是/ 有可能是除號 也有可能是註釋
else if(buffer=='/')
{
row++;word+=buffer;
buffer=fgetc(fp);
//這種情況是除號
if(buffer!='*' && buffer !='/')
{
aa=fseek(fp,-1,SEEK_CUR);
return 4;
}
// 這一行剩下的全被註釋了
if(buffer=='/')
{
word.clear();
while((buffer=fgetc(fp)) && buffer!='\n' &&!feof(fp))
{
//真的什麼也沒有做,指針自動往後移動
}
if(feof(fp)){
flag=0;return 0;
}
else{
aa=fseek(fp,-1,SEEK_CUR);
}
line_++;row=1;
return 0;
}
if(buffer=='*')
{
bool flag5=1;
while(flag5)
{
word.clear();
buffer=fgetc(fp);
row++;
if(buffer=='\n'){line_++;row=1;}
if(buffer!='*')continue;
else {//接收到*
buffer=fgetc(fp);
row++;if(buffer=='\n'){line_++;row=1;}
if(buffer=='/'){//接收到*/
flag5=0;
}
else continue;
}
if(feof(fp)){flag=0;return 0;}
}
}
}
else {
word+=buffer;
row++;
return -1;
}
}
int main()
{
reservedn = set_reserve();//設置保留字
//cout<<"open "<<"code.txt"<<endl;
//cout<<"_____________________"<<endl<<endl;
flag=1;
FILE *fp;
if(!(fp=fopen("code.txt","r")))
{
cout<<"not found the file or other error "<<endl;
flag=0;
}
while(flag==1)
{
//flag2 返回的類型
flag2=scan(fp);//反覆調用函數提取單詞
if(flag2==1)
{
cout<<"( 2 ,"<<" \" "<<word<<" \" )"<<endl;
if(word.length()>20)
cout<<"ERROR Identifier length cannot exceed 20 characters"<<endl;
word.clear();
}
else if(flag2==3)
{
cout<<"( 1 ,"<<" \" "<<word<<" \" )"<<endl;
word.clear();
}
else if(flag2==4)
{
cout<<"( 4 ,"<<" \" "<<word<<" \" )"<<endl;
word.clear();
}
else if(flag2==2)
{
cout<<"( 3 ,"<<" \" "<<word<<" \" )"<<endl;
if(word[0]=='0' && word[1])
cout<<"ERROR: The first digit cannot be 0!"<<endl;
word.clear();
}
else if(flag2==6)
{
cout<<"( 5 ,"<<" \" "<<word<<" \" )"<<endl;
word.clear();
}
else if(flag2==5)
{
cout<<"( 4 ,"<<" \" "<<word<<" \" )"<<endl;
word.clear();
}
//非法字符
else if(flag2==-1)
{
cout<<"Illegal character "<<"line "<<line_<<" row "<<row-1<<" "<<word<<endl;
word.clear();
}
}
int a=fclose(fp);
//cout<<"_____________________"<<endl;
system("pause");
return 0;
}
python界面
-
中午午睡意識朦朧,思考着上午用EGE做界面舉步維艱,想着能不能有其他方式。突然一個想法竄出來,既然c++編譯成exe文件,那麼是不是能被python調用,用python界面加c++後端來完成,頃刻間睡意全無,立馬拿起手機一百度,果不其然。(之前從來沒涉及這個,這次突發奇想讓我很是自豪)
-
思路:繪製輸入框和輸出框,輸入框裏輸入源程序點擊按鈕將框內文本保存到code.txt,調用詞法分析程序的exe文件,輸出結果經處理輸出到文本框。
import os
import tkinter as tk
window = tk.Tk()
window.title('詞法分析')
window.minsize(500,500)
#點擊按鈕後執行的函數
def changeString():
text_output.delete('1.0','end')
text = text_input.get('1.0','end')
filename = 'D://我的文檔/code.txt'
with open(filename,'w') as f:
f.write(text)
os.chdir("D:\我的文檔\\")
main = "詞法分析.exe"
f = os.popen(main)
data = f.readlines()
data = data[0:-1:1]
f.close()
for i in data:
text_output.insert("insert",i)
#創建文本輸入框和按鈕
text_input = tk.Text(window, width=100, height=20)
text_output = tk.Text(window, width=100, height=20)
button = tk.Button(window,text="詞法分析",command=changeString,padx=32,pady=4,bd=4)
#把Text組件和按鈕放在窗口上,然後讓窗口打開,並處理在窗口內發生的所有事件;
text_input.pack()
text_output.pack()
button.pack()
window.mainloop()
小提示
- 讀程序時別過於抓住line_和row,這只是給分析的詞語定位,不影響整體功能
- 程序中有一些組合運算符的判斷,+= -= >= &&等,和單個運算符做好區分
- 要想清楚爲什麼要對文件指針進行回退操作
- 保留字:1 標識符:2 數字:3 運算符:4 分隔符:5
- 想要輸出成什麼樣自己改吧
- 我也不知道直接分享給你們好不好,重複造輪子到底是學習還是浪費時間,都有吧。其實,萬丈高樓平地起很難的,尤其是沒思路,有思路還好。總之,代碼獻上讀懂了就是你的知識了
借鑑
感謝思路來自這裏