編譯原理學習一,去除程式碼中的註釋
開始學習編譯原理了耶~ 關於編譯原理的所有練習,按照老規矩,還是用我最喜歡的C#語言來實現,執行在.NetCore平臺上~ 關於這個系列的所有程式碼已經上傳到github了,專案主頁:
本次題目
對C或C++等高階程式設計語言編寫的源程式中的//註釋和/… /註釋進行刪除,保留刪除後的源程式。要求以檔案形式進行儲存。
思路分析
程式主要功能就是消除已經編寫好的源程式中的註釋。在源程式中註釋有兩種形式,一種是單行註釋,用“//”表示,另一種是多行註釋,用“/… /”表示。針對這兩種形式,程式中用了if..else..語句加以判斷,並做出相應的處理。在這裡還有可能出現另一種情況,上述兩種註釋符號可能出現在引號中,出現在引號中的註釋符號並沒有註釋功能,因此在引號中出現的註釋符號不應該被消除。所以,這次編寫的程式將要分三種情況分析。
第一種情況,單行註釋:
if (ch != temp) { // 這裡就是單行註釋 ofile.put(ch); ch = ifile.get(); } 複製程式碼
或者
if (ch != temp) { /* 這裡就是單行註釋 */ ofile.put(ch); ch = ifile.get(); } 複製程式碼
第二種情況,塊註釋:
if (ifile.fail() || ofile.fail()) { cerr << "open file fail\n"; return EXIT_FAILURE; /*返回值EXIT_FAILURE(在cstdlib庫中定義),用於向作業系統報* 告開啟檔案失敗*/ } 複製程式碼
第三種情況,行後註釋:
ifile.close(); // 關閉檔案 ofile.close(); cout << "/////*////ret/rtr////"; system("pause"); return 0; 複製程式碼
還有一個關鍵的注意點
可以看到這一行
cout << "/////*////ret/rtr////"; 複製程式碼
這個字串用雙引號包起來的程式碼中有很多斜槓,所以要避免將這些斜槓識別為註釋。 這裡我用的方法是在處理註釋前先把包含註釋符號的字串替換掉,等註釋刪除之後,再添加回去。
實現程式碼
註釋寫得很詳細啦,配合上面的思路分析,我就不再繼續分析程式碼了~
var sReader = new StreamReader(filePath); var newSource = ""; var inBlock = false; var replaceFlag = false; var tempLine = ""; // 用於儲存被替換的特殊行程式碼 while (!sReader.EndOfStream) { var line = sReader.ReadLine(); if (line.Length == 0) continue; // 去除空行 var quotationPattern = "^(.*?)\".*//.*\""; var quotationResult = Regex.Match(line, quotationPattern); if (quotationResult.Success) { System.Console.WriteLine("替換特殊程式碼,雙引號中包裹註釋斜槓"); tempLine = quotationResult.Groups[0].Value; replaceFlag = true; line = Regex.Replace(line, quotationPattern, REPLACEMENT); } // 單行註釋 if (line.Trim().StartsWith(@"//")) continue; if (line.Trim().StartsWith(@"/*") && line.EndsWith(@"*/")) continue; // 註釋塊 if (Regex.Match(line.Trim(), @"^/\*").Success) inBlock = true; if (Regex.Match(line.Trim(), @"\*/$").Success) { inBlock = false; continue; } // 行後註釋 // 使用非貪婪模式(.+?)匹配第一個// var pattern = @"^(.*?)//(.*)"; // var pattern = @"[^(.*?)//(.*)]|[^(.*?)/\*(.*)\*/]"; var result = Regex.Match(line, pattern); if (result.Success) { System.Console.WriteLine("發現行後註釋:{0}", result.Groups[2]); line = result.Groups[1].Value; } // 還原被替換的程式碼 if (replaceFlag) { System.Console.WriteLine("還原特殊程式碼"); line = line.Replace(REPLACEMENT, tempLine); replaceFlag = false; } if (inBlock) continue; newSource += line + Environment.NewLine; } var outputPath = "output/exp1.src"; System.Console.WriteLine("去除註釋完成,建立新檔案。"); using (var sWriter = new StreamWriter(outputPath)) { sWriter.Write(newSource); } System.Console.WriteLine("操作完成!檔案路徑:{0}", outputPath); 複製程式碼
結果測試
原始檔
#include <iostream> #include <fstream> #include <iomanip> #include <cstdlib> using namespace std; int main() { cout << '/'; ifstream ifile; //建立檔案流物件 ofstream ofile; ifile.open("f:\\上機實驗題\\C++\\ConsoleApplication2\\ConsoleApplication2\\源.cpp"); //開啟F盤根目錄下的fileIn.txt檔案 ofile.open("f:\\上機實驗題\\C++\\ConsoleApplication2\\ConsoleApplication2\\源.obj"); if (ifile.fail() || ofile.fail()) { //測試開啟操作是否成功 cerr << "open file fail\n"; return EXIT_FAILURE; /*返回值EXIT_FAILURE(在cstdlib庫中定義),用於向作業系統報* 告開啟檔案失敗*/ } char ch; ch = ifile.get(); //進行讀寫操作 while (!ifile.eof()) { if (ch == 34) {//雙引號中若出現“//”,雙引號中的字元不消除 char temp = ch; //第一個雙引號 ofile.put(ch); ch = ifile.get(); while (!ifile.eof()) { if (ch != temp) { //尋找下一個雙引號 ofile.put(ch); ch = ifile.get(); } else { ofile.put(ch); break; } } ch = ifile.get(); continue; //雙引號情況結束,重新新一輪判斷 } if (ch == 47) { //出現第一個斜槓 char temp2 = ch; ch = ifile.get(); if (ch == 47) { //單行註釋情況 ch = ifile.get(); while (!(ch == '\n')) ch = ifile.get(); } else if (ch == '*') { //多行註釋情況 while (1) { ch = ifile.get(); while (!(ch == '*')) ch = ifile.get(); ch = ifile.get(); if (ch == 47) break; } ch = ifile.get(); } else { ofile.put(temp2); //temp2儲存第一個斜槓,當上述兩種情況都沒有時,將此斜槓輸出 } //ch = ifile.get(); } //cout << ch << endl; ofile.put(ch);//將字元寫入檔案流物件中 ch = ifile.get(); //從輸入檔案物件流中讀取一個字元 } ifile.close(); //關閉檔案 ofile.close(); cout << "/////*////ret/rtr////"; system("pause"); return 0; } 複製程式碼
處理後的結果
#include <iostream> #include <fstream> #include <iomanip> #include <cstdlib> using namespace std; int main() { cout << '/'; ifstream ifile; ofstream ofile; ifile.open("f:\\上機實驗題\\C++\\ConsoleApplication2\\ConsoleApplication2\\源.cpp"); ofile.open("f:\\上機實驗題\\C++\\ConsoleApplication2\\ConsoleApplication2\\源.obj"); if (ifile.fail() || ofile.fail()) { cerr << "open file fail\n"; return EXIT_FAILURE; } char ch; ch = ifile.get(); while (!ifile.eof()) { if (ch == 34) { char temp = ch; ofile.put(ch); ch = ifile.get(); while (!ifile.eof()) { if (ch != temp) { ofile.put(ch); ch = ifile.get(); } else { ofile.put(ch); break; } } ch = ifile.get(); continue; } if (ch == 47) { char temp2 = ch; ch = ifile.get(); if (ch == 47) { ch = ifile.get(); while (!(ch == '\n')) ch = ifile.get(); } else if (ch == '*') { while (1) { ch = ifile.get(); while (!(ch == '*')) ch = ifile.get(); ch = ifile.get(); if (ch == 47) break; } ch = ifile.get(); } else { ofile.put(temp2); } } ofile.put(ch); ch = ifile.get(); } ifile.close(); ofile.close(); cout << "/////*////ret/rtr////"; system("pause"); return 0; } 複製程式碼