編譯原理學習一,去除代碼中的注釋
前言
開始學習編譯原理了耶~
關於編譯原理的所有練習,按照老規矩,還是用我最喜歡的C#語言來實現,運行在.NetCore平台上~關於這個系列的所有代碼已經上傳到github了,項目主頁:https://github.com/Deali-Axy/CompilerConstructionLearning
本次題目
對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
";
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
";
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 ==
))
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
";
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 ==
))
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;
}
完整代碼
https://github.com/Deali-Axy/CompilerConstructionLearning/blob/master/code/exp/exp1/Exp1.cs
參考資料:
- 正則表達式元字元:https://www.runoob.com/regexp/regexp-metachar.html
- JavaScript去除注釋:https://segmentfault.com/a/1190000015611632
推薦閱讀: