欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C++如何去除cpp文件的注釋詳解

 更新時(shí)間:2022年09月30日 09:47:46   作者:「已注銷(xiāo)」  
在日常工作中,我們會(huì)給c/c++代碼寫(xiě)上一些注釋,但是往往為了保持最終的代碼盡可能小,我們需要?jiǎng)h除注釋,手動(dòng)刪除太緩慢了,下面這篇文章主要給大家介紹了關(guān)于C++如何去除cpp文件注釋的相關(guān)資料,需要的朋友可以參考下

問(wèn)題:讀取一個(gè)cpp文件,去除其中所有的單行注釋?zhuān)?/)和多行注釋?zhuān)?**/),將去除注釋后的內(nèi)容寫(xiě)入一個(gè)新文件。

注意:

不能去除字符串中的任何字符,如 "asdf//gh/**/k" 需要原樣輸出

能夠識(shí)別轉(zhuǎn)義的雙引號(hào)(\")和非轉(zhuǎn)義的雙引號(hào)("),如:

  • '\"'(單引號(hào)中的轉(zhuǎn)義雙引號(hào)字符)
  • "asdf\"" (字符串中的轉(zhuǎn)義雙引號(hào)不能表示字符串結(jié)束)
  • \"aaa"bcdef" (同理,轉(zhuǎn)義雙引號(hào)不能表示字符串開(kāi)始)

命令行參數(shù)為:

  • 參數(shù)1:原文件名
  • 參數(shù)2:新文件名

一、文件流

引入<fstream>頭文件,新建ifstream和ofstream對(duì)象,如果不能打開(kāi)文件,則結(jié)束整個(gè)函數(shù)。 

#include <iostream>
#include <fstream>
 
using namespace std;
 
bool stripComment(string infile,string outfile){
    ifstream ifs;
    ifs.open(infile,ios::in);
    if(!ifs.is_open()){
        cout<<"fail to read!"<<endl;
        return true;
    }
 
    ofstream ofs;
    ofs.open(outfile,ios::out|ios::trunc);//寫(xiě)文件,如果已經(jīng)存在同名的文件,則刪除它創(chuàng)建新文件
    if(!ofs.is_open()){
        cout<<"fail to write!"<<endl;
        return true;
    }
 
    ifs.close();
    ofs.close();
 }
 
int main(int argc,char** argv){
    return 0;
}

二、具體邏輯

1.如何循環(huán)讀入字符

這是最關(guān)鍵的一步。我們利用ifstream對(duì)象的get()方法無(wú)條件地讀取文件流中的一個(gè)字符。什么叫無(wú)條件?這是與>>運(yùn)算符相區(qū)分的,>>會(huì)自動(dòng)跳過(guò)所有空白字符,也就是說(shuō),它讀不到空格、\t、\n,這不符合我們的需求。 

同時(shí),ifstream對(duì)象有一個(gè)putback()方法,它就是將一個(gè)字符重新放回文件流的末尾,下一次get(),我們還會(huì)讀到它:這有時(shí)候很有用。

get(char c) 會(huì)把文件流的末尾一個(gè)字符取出,放到char型變量c中。這個(gè)函數(shù)的返回值可以被轉(zhuǎn)換成一個(gè)布爾值,表示流的狀態(tài):如果流狀態(tài)正常,則返回true;如果流狀態(tài)不正常(bad,fail,讀到文件尾符號(hào)),返回false。

char temp1{};
 while(ifs.get(temp1)){
    ofs<<temp1;
 }

最簡(jiǎn)單的情況是,利用get()循環(huán)讀入字符并存入temp1變量中,再把temp1寫(xiě)入新文件中。最終我們會(huì)得到與原文件一樣的副本。

2.處理單行和多行注釋

這兩種東西有同一個(gè)特點(diǎn):都是以 / 開(kāi)頭的。所以我們讀取到 / 這個(gè)字符時(shí),就要小心一點(diǎn):它是不是意味著我讀到了一個(gè)注釋?zhuān)?/p>

我們可以在此基礎(chǔ)上再讀一個(gè)字符,存入另一個(gè)字符變量temp2中,看看它是什么情況:

  • / :一定讀到了一個(gè)單行注釋
  • * :一定讀到了一個(gè)多行注釋
  • 其他字符:剛剛讀到的 / 只是一個(gè)普通的正斜杠

對(duì)于第三種情況, 我們這樣善后:

  • 立刻輸出temp1(/)
  • 把temp2放回文件流中等待下一次讀取,因?yàn)樗赡苡杏茫ū热?,是個(gè)雙引號(hào))
  • 跳轉(zhuǎn)到一開(kāi)始的while循環(huán)

 對(duì)于第一種情況:

  • 一直向后讀字符,直到讀到一個(gè)換行符\n
  • 將這個(gè)換行符放回文件流
  • 跳轉(zhuǎn)到一開(kāi)始的while循環(huán)

第二種情況最麻煩,我們必須找一個(gè)辦法確定什么時(shí)候多行注釋才能結(jié)束。C++的語(yǔ)法規(guī)定, /* 與最近的 */ 之間是多行注釋?zhuān)敲次覀冎恍枵易罱?*/ 即可。

  1. while循環(huán)一直向后讀字符,直到找到一個(gè) *
  2. 再讀一個(gè)字符,放入字符變量temp3中。
    1. temp3是一個(gè) / ,恭喜!我們已經(jīng)找到多行注釋的完整范圍,只要簡(jiǎn)單地break掉當(dāng)前的while循環(huán),再跳轉(zhuǎn)到開(kāi)始的while循環(huán)就可以。不用輸出任何東西。
    2. 不好,temp3不是 / ,這意味著我們沒(méi)有找到多行注釋的終止處。此時(shí)應(yīng)該將temp3放回文件流,返回步驟1:這是很必要的,如果temp3正好是一個(gè)* ,它之后恰好是一個(gè) / 呢?我們不能丟棄任何“可能有用”的字符!
while(ifs.get(temp1)){
        //處理 "http://" 或 "/**/"
        if(temp1=='/'){
 
            checkStatus=true;
            ifs.get(temp2);//再讀一個(gè)字符
 
            if(temp2=='/'){//處理"http://"
 
                checkStatus=false;
                while(ifs.get(temp2)){//一直向后讀字符,直到讀到一個(gè)換行符\n
                    if(temp2=='\n') break;
                }
                ifs.putback('\n');//將換行符放回文件流
                continue;//跳轉(zhuǎn)到一開(kāi)始的while循環(huán)
 
            }else if(temp2=='*'){//處理 "/**/"
 
                checkStatus=true;
                while(ifs.get(temp3)){
                    if(temp3=='*'){//找到一個(gè)*
                        ifs.get(temp4);
                        if(temp4=='/'){//找到一個(gè)/
                            checkStatus=false;
                            break;
                        }else{//沒(méi)找到/
                            ifs.putback(temp4);
                            continue;
                        }
                    }
                }
                continue;
            }else{//只讀到了一個(gè) /
                checkStatus=false;
                ifs.putback(temp2);//把temp2放回文件流中等待下一次讀取
            }
        }
        ofs<<temp1;
    }

3.注意字符串

我們?cè)?中的操作會(huì)破壞字符串中的一些內(nèi)容,比如:

處理前:"asdf//gh/**/k"

處理后(//被當(dāng)成了單行注釋?zhuān)?quot;asdf

我們的需求是保留字符串原封不動(dòng)輸出。

因此,當(dāng)temp1沒(méi)有讀到 / 時(shí)(即沒(méi)有進(jìn)入處理注釋的邏輯),temp1讀到了一個(gè) " ,我們就要小心了:后面的內(nèi)容是字符串,原樣輸出即可。

  • 立刻輸出temp1。
  • 循環(huán)讀入后面的字符,存入temp2中,立即輸出temp2。
  • 檢查剛剛輸出的temp2是不是 " ,如果是,結(jié)束當(dāng)前循環(huán)并跳轉(zhuǎn)到一開(kāi)始的while循環(huán)
    while(ifs.get(temp1)){
        //處理 "http://" 或 "/**/"
        //省略...
 
        //處理字符串
        if(temp1=='\"'){
            ofs<<temp1;//立刻輸出第一個(gè)雙引號(hào)
            checkStatus=true;
            while(ifs.get(temp2)){
                ofs<<temp2;//立刻輸出temp2
 
                if(temp2=='\"'){//temp2是結(jié)尾的雙引號(hào)
                    checkStatus=false;
                    break;
                }
            }
        continue;//返回一開(kāi)始的while循環(huán)
        }
        ofs<<temp1;
    }

4.注意轉(zhuǎn)義雙引號(hào)

以上的兩塊邏輯能解決一般問(wèn)題,但在以下的例子中會(huì)出錯(cuò):

char c='\"';
const char* p1 = "sdjksd\"\\d//fj/*kdhjk\"dsfjl*/dks";

int main()
{
    cout << "http://The number of queens (4 -- 12) :// " ;
}

最后的結(jié)果是:

char c='\"';
const char* p1 = "sdjksd\"\\d//fj/*kdhjk\"dsfjl*/dks";

int main()
{
    cout << "
}

為什么呢?其實(shí)是轉(zhuǎn)義雙引號(hào)(\")在作怪,因?yàn)槲覀兡壳暗倪壿媽?strong>轉(zhuǎn)義雙引號(hào)與表示字符串開(kāi)頭與結(jié)尾的普通雙引號(hào)混為一談,導(dǎo)致字符串的邊界出現(xiàn)混亂。

以下紅色部分表示程序所認(rèn)為的字符串,藍(lán)色部分表示程序認(rèn)為是注釋的地方():

可以看出這個(gè)問(wèn)題挺嚴(yán)重的,首要之急就是區(qū)分轉(zhuǎn)義雙引號(hào)和普通雙引號(hào)。

C++的文件流是逐個(gè)字符讀取的,也就是說(shuō),\" 在get()時(shí),第一次會(huì)得到 \ ,第二次會(huì)得到 "

另一個(gè)有用的信息就是,C++的字符串邊界永遠(yuǎn)是普通雙引號(hào),而不是轉(zhuǎn)義雙引號(hào)。

1>防止轉(zhuǎn)義雙引號(hào)作為字符串開(kāi)頭

  • 當(dāng)沒(méi)有進(jìn)入處理注釋的邏輯時(shí),讀到了一個(gè) \
  • 立刻輸出 \ 本身
  • 讀取下一個(gè)字符存入temp2中
  • 立刻輸出temp2
  • 跳轉(zhuǎn)到一開(kāi)始的while循環(huán)

為什么這樣做呢?分析一下,因?yàn)?\ 與它后面的字符已經(jīng)被轉(zhuǎn)義(它們是一個(gè)整體),\ 后面的字符不能被認(rèn)為是代碼語(yǔ)法的一部分。比如:\ 之后的 " 不能被認(rèn)為是普通的雙引號(hào),\ 之后的 / 也不能被認(rèn)為是一個(gè)代表注釋開(kāi)頭的 /。反斜杠后面的字符從語(yǔ)法上來(lái)說(shuō)應(yīng)該直接輸出。

注:處理注釋時(shí)不需這種邏輯,注釋里的 \ 沒(méi)有什么特殊的意義,和普通字符地位一樣。它不能影響后面那個(gè)字符的含義。

/*aaaaa\*/

仍然表示一個(gè)多行注釋

2>防止轉(zhuǎn)義雙引號(hào)作為字符串結(jié)尾

要在字符串的輸出邏輯中增加檢查機(jī)制。

  • 剛剛輸出的字符(temp2)如果是一個(gè) \
  • 讀取下一個(gè)字符并直接輸出
  • 繼續(xù)執(zhí)行當(dāng)前的while輸出循環(huán),直到temp2是一個(gè)雙引號(hào)為止 
    while(ifs.get(temp1)){
        //處理 "http://" 或 "/**/"
        //...省略
 
        //防止\"作為字符串開(kāi)頭
        if(temp1=='\\'){
            checkStatus=true;
            ofs<<temp1;//直接輸出\
            //如果\后面有字符,直接輸出\后面的字符
            if(ifs.get(temp2)){
                ofs<<temp2;
                checkStatus=false;
            }
            continue;//跳轉(zhuǎn)到一開(kāi)始的while循環(huán)
        }
 
        //處理字符串
        if(temp1=='\"'){
            ofs<<temp1;
            checkStatus=true;
            while(ifs.get(temp2)){
                ofs<<temp2;
 
                //如果剛剛輸出的是一個(gè)\
                if(temp2=='\\'){
                    if(ifs.get(temp3)) ofs<<temp3;//直接輸出后一個(gè)字符
                    continue;//繼續(xù)當(dāng)前的while循環(huán)
                }
 
                if(temp2=='\"'){//輸出了一個(gè)普通雙引號(hào),表示字符串結(jié)束
                    checkStatus=false;
                    break;
                }
            }
        continue;
        }
        ofs<<temp1;
    }

5.增加簡(jiǎn)單的查錯(cuò)功能

當(dāng)我們發(fā)現(xiàn)了 /* 或 " 時(shí),這意味著我們開(kāi)始進(jìn)行注釋邊界的確定或字符串邊界的確定。當(dāng)發(fā)現(xiàn)了一個(gè) \ 之后,\ 后面理應(yīng)有一個(gè)字符。

如果我們:

  • 找到了 " 卻始終未找到對(duì)應(yīng)的 "
  • 找到了 /* 卻始終沒(méi)有找到 */
  • 找到了 \ 后面卻無(wú)字符

這三種情況意味著原文件的語(yǔ)法一定有問(wèn)題,我們用一個(gè)變量checkStatus表示。當(dāng)開(kāi)始進(jìn)行"尋找"過(guò)程,checkStatus被設(shè)為true。當(dāng)尋找結(jié)束后,變量的值被設(shè)為false。如果文件流結(jié)束,此變量依然為true,那么應(yīng)提醒用戶(hù),文件的語(yǔ)法有問(wèn)題。

完整代碼如下:

#include <iostream>
#include <fstream>
 
using namespace std;
 
bool stripComment(string infile,string outfile){
    ifstream ifs;
    ifs.open(infile,ios::in);
    if(!ifs.is_open()){
        cout<<"fail to read!"<<endl;
        return true;
    }
 
    ofstream ofs;
    ofs.open(outfile,ios::out|ios::trunc);
    if(!ofs.is_open()){
        cout<<"fail to write!"<<endl;
        return true;
    }
 
    bool checkStatus=false;
    char temp1{};
    char temp2{};
 
    char temp3{};
 
    while(ifs.get(temp1)){
 
 
        //handle "http://" OR "/**/"
        if(temp1=='/'){
 
            checkStatus=true;
            ifs.get(temp2);
 
 
            if(temp2=='/'){//handle "http://"
 
                checkStatus=false;
                while(ifs.get(temp2)){
                    if(temp2=='\n') break;
                }
                ifs.putback('\n');
                continue;
 
            }else if(temp2=='*'){//handle "/**/"
 
                checkStatus=true;
                while(ifs.get(temp2)){
                    if(temp2=='*'){
                        ifs.get(temp3);
                        if(temp3=='/'){
                            checkStatus=false;
                            break;
                        }else{
                            ifs.putback(temp3);
                            continue;
                        }
                    }
                }
                continue;
            }else{//only read a '/'
                checkStatus=false;
                ifs.putback(temp2);
            }
        }
 
        //handle possible '\"'
        if(temp1=='\\'){
            checkStatus=true;
            ofs<<temp1;
            //output char after '\'
            if(ifs.get(temp2)){
                ofs<<temp2;
                checkStatus=false;
            }
            continue;
        }
 
        //handle true ""
        if(temp1=='\"'){
            ofs<<temp1;
            checkStatus=true;
            while(ifs.get(temp2)){
                ofs<<temp2;
                //handle things after '\'
                if(temp2=='\\'){
                    if(ifs.get(temp3)) ofs<<temp3;
                    continue;
                }
                if(temp2=='\"'){
                    checkStatus=false;
                    break;
                }
            }
        continue;
        }
 
        ofs<<temp1;
 
    }
 
    ifs.close();
    ofs.close();
    return checkStatus;
}
 
int main(int argc,char** argv){
 
 
    if(argc!=3){//檢查是否輸入了兩個(gè)文件名
        cout<<"argument invalid!"<<endl;
        return -1;
    }
 
    bool res=stripComment(argv[1],argv[2]);//去除注釋
 
    if(res==true){//文件語(yǔ)法有錯(cuò)
        cout<<"maybe wrong."<<endl;
    }
    return 0;
}

三、正則實(shí)現(xiàn)(Java)

這是題外話(huà),但其實(shí)正則表達(dá)式也能實(shí)現(xiàn)這一功能。

由于C++的正則似乎不支持后發(fā)斷言,所以使用了Java的正則類(lèi)。

主要思路:

  1. 讀取待去除注釋的文本,存入一個(gè)字符串中。
  2. 創(chuàng)建兩個(gè)正則表達(dá)式,一個(gè)匹配注釋和字符串,另一個(gè)只能匹配注釋。
  3. 對(duì)該字符串循環(huán)匹配第一個(gè)正則表達(dá)式,得到結(jié)果str。
  4. 若str符合第二個(gè)正則表達(dá)式,則記下str在原字符串中的起始下標(biāo)和結(jié)束下標(biāo),將二者都存入ArrayList對(duì)象中。
  5. 匹配結(jié)束后,遍歷原字符串,如果字符下標(biāo)位于”存儲(chǔ)下來(lái)的注釋區(qū)域的起始和結(jié)束下標(biāo)“之間,就不輸出這些字符。
package project1100;
 
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class mainTest {
    public static void main(String[] args) {
 
        String str2=
                "const char *qq=\"/**/\";\n" +
                "const char *pp=/**\"\\\"007*/\"/**/\";\n" +
                "const char *rr=/**'\\'007*/\"/**/\"\"http://\";";
        
        //匹配注釋和字符串
        Pattern pattern1=Pattern.compile("(/((/.*)|(\\*[\\S\\s]*?\\*/)))|((?<!\\\\)(\"([\\S\\s]*?)[^\\\\]\"))");
        //只能匹配注釋
        Pattern pattern2=Pattern.compile("(/((/.*)|(\\*[\\S\\s]*?\\*/)))");
        //用于存儲(chǔ)注釋區(qū)域下標(biāo)范圍的容器
        ArrayList<Integer> arrayList=new ArrayList<Integer>();
 
        Matcher matcher1=pattern1.matcher(str2);
 
        //匹配注釋和字符串
        while(matcher1.find()){
            //是注釋?zhuān)聵?biāo)存入容器中
            if(pattern2.matcher(matcher1.group(0)).matches()) {
                arrayList.add(matcher1.start(0));
                arrayList.add(matcher1.end(0));
 
            }
        }
 
        //選擇輸出
        label:for(int i=0;i<str2.length();i++){
            for(int j=0;j<arrayList.size()/2;j++){
                if(i>=arrayList.get(j*2)&&i<arrayList.get(j*2+1)){
                    continue label;
                }
            }
            System.out.print(str2.charAt(i));
        }
        /*結(jié)果
            const char *qq="/**/";
            const char *pp="/**/";
            const char *rr="/**/""http://";
        */
    }
}

 圖解:

 可能仍有疏漏,請(qǐng)各位大佬指正。

結(jié)語(yǔ):

去除代碼中的注釋?zhuān)此坪?jiǎn)單,實(shí)則要考慮諸多情況。這是一個(gè)極其考驗(yàn)思維細(xì)致度的問(wèn)題,希望大家都能考慮到每一種情況,找到自己最好理解的思路。

到此這篇關(guān)于C++如何去除cpp文件注釋的文章就介紹到這了,更多相關(guān)C++去除cpp文件注釋內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 基于C語(yǔ)言打造高效便捷的通訊錄管理系統(tǒng)

    基于C語(yǔ)言打造高效便捷的通訊錄管理系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了如何基于C語(yǔ)言打造高效便捷的通訊錄管理系統(tǒng),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-02-02
  • C++使用sort對(duì)容器排序的實(shí)現(xiàn)

    C++使用sort對(duì)容器排序的實(shí)現(xiàn)

    C++ STL 標(biāo)準(zhǔn)庫(kù)中的sort()函數(shù)專(zhuān)門(mén)用來(lái)對(duì)容器或普通數(shù)組中指定范圍內(nèi)的元素進(jìn)行排序,本文就詳細(xì)的介紹一下怎么實(shí)現(xiàn),需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-05-05
  • QT中如何讀寫(xiě)ini配置文件

    QT中如何讀寫(xiě)ini配置文件

    ini文件在windows系統(tǒng)中可以存儲(chǔ)需要持久保存的配置信息,QT界面中如何實(shí)現(xiàn)手動(dòng)讀取參數(shù)存放的位置,感興趣的小伙伴們可以參考一下
    2021-11-11
  • 詳解C++編程中斷言static_assert的使用

    詳解C++編程中斷言static_assert的使用

    這篇文章主要介紹了C++編程中斷言static_assert的使用,斷言在debug時(shí)非常有用,是C++入門(mén)學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下
    2016-01-01
  • floyd算法實(shí)現(xiàn)思路及實(shí)例代碼

    floyd算法實(shí)現(xiàn)思路及實(shí)例代碼

    這篇文章主要介紹了floyd算法實(shí)現(xiàn)思路及實(shí)例代碼,有需要的朋友可以參考一下
    2014-01-01
  • C語(yǔ)言超詳細(xì)講解遞歸算法漢諾塔

    C語(yǔ)言超詳細(xì)講解遞歸算法漢諾塔

    漢諾塔問(wèn)題是一個(gè)經(jīng)典的問(wèn)題。漢諾塔(Hanoi Tower),又稱(chēng)河內(nèi)塔,源于印度一個(gè)古老傳說(shuō)。本文將用Java求解這一問(wèn)題,感興趣的可以學(xué)習(xí)一下
    2022-05-05
  • C++動(dòng)態(tài)內(nèi)存分配超詳細(xì)講解

    C++動(dòng)態(tài)內(nèi)存分配超詳細(xì)講解

    給數(shù)組分配多大的空間?你是否和初學(xué)C時(shí)的我一樣,有過(guò)這樣的疑問(wèn)。這一期就來(lái)聊一聊動(dòng)態(tài)內(nèi)存的分配,讀完這篇文章,你可能對(duì)內(nèi)存的分配有一個(gè)更好的理解
    2022-08-08
  • DSP中浮點(diǎn)轉(zhuǎn)定點(diǎn)運(yùn)算--舉例及編程中的心得

    DSP中浮點(diǎn)轉(zhuǎn)定點(diǎn)運(yùn)算--舉例及編程中的心得

    本文主要講解DSP浮點(diǎn)轉(zhuǎn)定點(diǎn)運(yùn)算舉例及編程中的心得 ,具有參考價(jià)值,需要的朋友可以參考一下。
    2016-06-06
  • C++ tuple元組的基本用法(總結(jié))

    C++ tuple元組的基本用法(總結(jié))

    這篇文章主要介紹了C++ tuple元組的基本用法(總結(jié)),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • 對(duì)C語(yǔ)言中sizeof細(xì)節(jié)的三點(diǎn)分析介紹

    對(duì)C語(yǔ)言中sizeof細(xì)節(jié)的三點(diǎn)分析介紹

    以下是對(duì)C語(yǔ)言中sizeof的細(xì)節(jié)進(jìn)行了詳細(xì)的分析介紹,需要的朋友可以參考下
    2013-07-07

最新評(píng)論