編譯原理學習一,去除代碼中的註釋

前言

開始學習編譯原理了耶~
關於編譯原理的全部練習,按照老規矩,仍是用我最喜歡的C#語言來實現,運行在.NetCore平臺上~
關於這個系列的全部代碼已經上傳到github了,項目主頁:html

https://github.com/Deali-Axy/CompilerConstructionLearning

本次題目

對C或C++等高級程序設計語言編寫的源程序中的//註釋和/ /註釋進行刪除,保留刪除後的源程序。要求以文件形式進行保存。

思路分析

程序主要功能就是消除已經編寫好的源程序中的註釋。在源程序中註釋有兩種形式,一種是單行註釋,用「//」表示,另外一種是多行註釋,用「/ /」表示。針對這兩種形式,程序中用了if..else..語句加以判斷,並作出相應的處理。在這裏還有可能出現另外一種狀況,上述兩種註釋符號可能出如今引號中,出如今引號中的註釋符號並無註釋功能,所以在引號中出現的註釋符號不該該被消除。因此,此次編寫的程序將要分三種狀況分析。

第一種狀況,單行註釋:

if (ch != temp)
{
    // 這裏就是單行註釋
    ofile.put(ch);
    ch = ifile.get();
}

或者ios

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;

還有一個關鍵的注意點

能夠看到這一行git

cout << "/////*////ret/rtr////";

這個字符串用雙引號包起來的代碼中有不少斜槓,因此要避免將這些斜槓識別爲註釋。
這裏我用的方法是在處理註釋前先把包含註釋符號的字符串替換掉,等註釋刪除以後,再添加回去。github

實現代碼

註釋寫得很詳細啦,配合上面的思路分析,我就再也不繼續分析代碼了~正則表達式

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;
}

完整代碼

https://github.com/Deali-Axy/CompilerConstructionLearning/blob/master/code/exp/exp1/Exp1.cssegmentfault

參考資料:微信

歡迎與我交流

相關文章
相關標籤/搜索