c去除註釋

怎麼編寫一個能去掉C++程序裏註釋的程序。從cin讀入,刪除所有的//和/*   */註釋,然後將其寫到cout.不要去掉位於產量中的註釋。

取出一行,檢查是否含有“//和/*   */”註釋
如果有:
            確定其位置(strstr()),用數組取出位置前面的,cout < <
否則:
            cout < <

大致流程就是這樣的了吧

沒這麼簡單吧,
如果你程序裏面有字符串裏面也有/*呢

比如char*   string   =   "//*/*// ";

總的程序思想感覺不會太難的,處理過程有點兒向編譯器中的詞法分析,只不過,這個比編譯器設計好簡單的多.只要找出你個關鍵字就可以了.

首先,讀出一行源程序代碼;

然後,按順序一個一個讀字符,若出現連續兩個 "/ "符號,且出現的位置不在一個完整的程序語句中時,可以確定爲註釋行(在C++的源代碼中);若出現的位置是一個不完整的程序語句時,需要判斷是否是處在一個字符串中,若不是,則連續 "/ "符號之後的所有字符都是註釋;若是,則可能是程序中使用的字符串,不能刪除.   對於/*   */結構來說,要好處理一些,只要找到 "/* "就可以了,找到之後,將其後所有的字符都假設爲註釋字符,一直遇到 "*/ "爲止.

最後,就是在上面一步處理的同時,講有用的程序代碼保存起來.

我寫過一個去除註釋的模塊,應該包含了我所想到的所有情況。
雖然不能提供代碼(出於職業道德),不過可以提供一下思路。
1先列出註釋的所有可能情況,比如printf( "/*abc*/ ")就不能算註釋,還有很多
這種情況,
2 畫好狀態轉換圖,確定讀入特定的字符之後,下一步應該判斷哪個字符。比如,讀如了/之後,下一步
判斷是否是/。如果是的話,把後面同行的內容刪掉。如果是*的話,就一直刪到下一對字符*/
#include <iostream>
using   namespace   std;
int   main()
{
char   c;
while(cin.get(c))
{if   (c== ' " ')    
{cout < <c;
while(cin.get(c))
{if(c== ' " ')break;
cout < <c;
}
}
if   (c== '/ ' ')    
{cout < <c;
while(cin.get(c))
{if(c== '/ ' ')break;
cout < <c;
}
}
if(c== '/ ')
{
cin.get(c);
if(c== '* ')
{
cout < < '   ';
while(cin.get(c))
{
if(c== '* ')
{
cin.get(c);
if(c!= '/ ')cin.putback(c);
else   break;
}
}
}
else   if(c== '/ ')
{
while(cin.get(c))
if(c== '/n '){cout < < '/n ';break;}
}
else   cout < < '/ ' < <c;
}
else   cout < <c;
}
}
我寫了一個,但似乎有點問題,先不考慮用bat文件erasecomments <source.cpp   > target.cpp,將source.cpp轉換成target.cpp。至考慮從屏幕輸入輸出的問題,比如輸入 "dfaf/*fj;lsaf*/,但輸出的仍是 "dfaf/*fj;lsaf*/,雖說在C++語法中,這種東西是不會存在的,但我困惑的是爲什麼會出現這種情況,而且在輸入完類似 "dfaf/*fj;lsaf*/的語句後,程序就會失效,譬如輸入/*abcd*/,卻依然輸出/*abcd*/,但程序如果沒有輸入過類似 "dfaf/*fj;lsaf*/的語句,程序是完全有效的。

 

#include   <iostream>
#include   <fstream>
#include   <string>
using   namespace   std;

class   CppfileFilter
{
public:
CppfileFilter(istream*   i=0,ostream*   o1=0,ostream*   o2=0);
//輸入源,文件輸出源,註釋輸出源
bool   Work();
string::size_type   LineNumber()   const;
//出現錯誤時返回行數
private:
istream   *from;
ostream   *to1,*to2;
string   Line;
string::size_type   pos,size,LineNum;
enum   Status{Normal,DivStart,DivDiv,Quotation,DoubleQuotation,Finish};
/*
枚舉值代表     正常               /*           //           '                   "狀態
*/
Status   S;
void   NewLine();
void   NormalFunc();//正常狀態
void   DivStartFunc();//進入   /*的狀態
void   DivDivFunc();//進入   //的狀態
void   QuotationFunc();//進入   '的狀態
void   DoubleQuotationFunc();//進入   "的狀態
typedef   void   (CppfileFilter::*ptrf)();
ptrf   array[5];
};

CppfileFilter::CppfileFilter(istream*   i,ostream*   o1,ostream*   o2)
{
from=i;
to1=o1;
to2=o2;
S=Normal;
array[0]=&CppfileFilter::NormalFunc;
array[1]=&CppfileFilter::DivStartFunc;
array[2]=&CppfileFilter::DivDivFunc;
array[3]=&CppfileFilter::QuotationFunc;
array[4]=&CppfileFilter::DoubleQuotationFunc;
}

string::size_type   CppfileFilter::LineNumber()   const
{
return   LineNum;
}

bool   CppfileFilter::Work()
{
LineNum=0;
if(!from   ||   !to1   &&   !to2)   return   false;
getline(*from,Line);
pos=0;
size=Line.size();
++LineNum;
while(true){
if(S==Finish)   return   false;//文件中註釋有錯誤
if(!*from)   return   true;//文件結束
(this-> *array[S])();//該過程會修改S值
}//while(true)
}

void   CppfileFilter::NewLine()
{
if(getline(*from,Line)){
pos=0;
size=Line.size();
++LineNum;
if(to1)   *to1 < < '/n ';
if(to2)   *to2 < < '/n ';
}
}

void   CppfileFilter::NormalFunc()
{
if(Line.empty()||pos==size){
NewLine();
}
else{
for(;pos <size;++pos){
if(Line[pos]== '/ '   &&   pos+1 <size){
if(Line[pos+1]== '* '){
if(to2)   *to2 < <Line[pos] < <Line[pos+1];
pos+=2;
S=DivStart;
return;
}
if(Line[pos+1]== '/ '){
if(to2)   *to2 < <Line[pos] < <Line[pos+1];
pos+=2;
S=DivDiv;
return;
}
}//if(Line[pos]== '/ '   &&   pos+1 <size)
else   if(Line[pos]== ' " '){
if(to1)   *to1 < <Line[pos];
++pos;
S=DoubleQuotation;
return;
}
else   if(Line[pos]== '/ ' '){
if(to1)   *to1 < <Line[pos];
++pos;
S=Quotation;
return;
}
if(to1)   *to1 < <Line[pos];
}//for(;pos <size;++pos)
}
}

void   CppfileFilter::DivStartFunc()
{
for(;pos <size;++pos){
if(to2)   *to2 < <Line[pos];
if(Line[pos]== '* '   &&   pos+1 <size   &&   Line[pos+1]== '/ '){
if(to2)   *to2 < <Line[pos+1];
pos+=2;
S=Normal;
return;
}
}
if(pos==size)   NewLine();
}

void   CppfileFilter::DivDivFunc()
{
for(;pos <size;++pos){
if(to2)   *to2 < <Line[pos];
}
S=Normal;
}

void   CppfileFilter::QuotationFunc()
{
if(Line[pos]== '// '   &&   pos+2 <size   &&   Line[pos+2]== '/ ' '){
if(to1)   *to1 < <Line[pos] < <Line[pos+1] < <Line[pos+2];
pos+=3;
S=Normal;
return;
}else   if(Line[pos]!= '// '   &&   pos+1 <size   &&   Line[pos+1]== '/ ' '){
if(to1)   *to1 < <Line[pos] < <Line[pos+1];
pos+=2;
S=Normal;
return;
}
S=Finish;
}
void   CppfileFilter::DoubleQuotationFunc()
{
for(;pos <size;++pos){
if(to1)   *to1 < <Line[pos];
if(Line[pos]== ' " '   &&   Line[pos-1]!= '// '){
++pos;
S=Normal;
return;
}
}
S=Finish;
}


int   main(int   argc,char*   argv[])
{
if(argc!=3)   return   1;
ifstream   R(argv[1]);
ofstream   W1(argv[2]);
ofstream   W2( "註釋.txt ");
CppfileFilter   T(&R,&W1,&W2);
if(T.Work()==false)   cout < < "error   at   line# " < <T.LineNumber();
}
 
 
 

if(!*from)   return   true;//文件結束
可以修改爲:
if(!*from){
      if(S==Normal)   return   true;
      return   false;
}

 

今天閒逛Linux寶庫,看到論壇裏有人在討論如何用 shell 腳本來處理 C 語言註釋,發帖時間是 08-10-23(以前怎麼都沒注意到,失敗...),但問題好像並沒被解決。正好這兩天玩 sed & awk,來小試一下身手。

C語句註釋
本文討論的是 C99 標準,它支持單行註釋(“// ...”)和塊註釋(“/*...*/”),並且當單行註釋以“/”結尾時也可以跨多行。測試代碼如下:

#include <stdlib.h>   
#include <stdio.h>   
int main (int argc, char *argv[])   
{   
// not show/   
not show/   
not show   
// not show   
/* not show */  
     int is; // not show   
     int/* not show */ ms; /* not show */  
     double ds; // not show/   
     not show/   
     not show   
     double dm; /* ...  
     not show  
     not show */ float fs; /**  
                            * now show  
                            */  
     float/**/ fm;   
     char cs[] = "aaa // /***/";   
     char cm1[] = /* not show */"hello*/";   
     char cm2[] = "/*redraiment"/* not show */;   
     /* printf("/////"); */  
     return EXIT_SUCCESS;   
}  
#include <stdlib.h>
#include <stdio.h>
int main (int argc, char *argv[])
{
// not show/
not show/
not show
// not show
/* not show */
     int is; // not show
     int/* not show */ ms; /* not show */
     double ds; // not show/
     not show/
     not show
     double dm; /* ...
     not show
     not show */ float fs; /**
                            * now show
                            */
     float/**/ fm;
     char cs[] = "aaa // /***/";
     char cm1[] = /* not show */"hello*/";
     char cm2[] = "/*redraiment"/* not show */;
 

 

 

  /* printf("/////"); */
     return EXIT_SUCCESS;
}

其中綠色部分就是註釋,經過處理後需要將它們全部移除或用替換成空字符。論壇原帖中沒有處理以“/”結尾的單行註釋,也沒處理註釋關鍵字出現在字符串中的情況。

工具的選擇
sed 是一個流編輯器,它能對文件進行“插入”、“刪除”、“替換”、“追加”等編輯操作;而 awk 是一門模式匹配的程序設計語言,它除了能編輯文本還可以統計信息,你可以把它看成基於文本文件的數據庫系統。原帖中作者使用 sed 來解決,因爲問題涉及的操作僅僅是刪除 C 代碼中的註釋。但由於以下原因導致 sed 心有餘而力不足:

一、不支持最小匹配
正則表達式默認採用貪心匹配策略,在正則的標準中通過在量詞後面加“?”來使用最小匹配策略,詳細規則介紹請參見這裏。問題中多行註釋必須使用最小匹配原則,如果關鍵詞只有一個字符,就可以通過排除字符集來模擬,比如我們經常用“"[^"]*"”來匹配一個字符串。可惜 C 語言的註釋關鍵詞都是多字符。

二、排序字符(Collating Symbols)只是一個美麗的夢想
排序字符用於字符列表(character list)中。按照文檔的描述,排序字符可將多個字符當一個字符來匹配。比如模式“[[.ch.]i]”可以匹配行“char”和“int”,但不能匹配“coho”。如果它能被支持,就可以把“/*”、“*/”、“//”都看做一個字符,通過(一)中的排除字符集來實現最小匹配。可惜到目前爲止,我接觸的工具中沒有一款支持這個特性,更不用說對正則支持平平的 sed 了。

三、字符串來搗亂
在《sed單行腳本學習筆記》中我給出了一段 sed 單行腳本,用於替換不在字符串中的模式。看起來正適合解決這個問題,但它的前提是模式要和待修改的文本完全匹配。由(一)、(二)兩個條件決定了 sed 實現的正則表達式無法匹配 C 語言所有類型的註釋。另外,sed 提供的控制語句是“b、t”,它們的功能是類似於 C 語言的 goto,因此它不能像“if ... else ...”一樣方便地判斷某個註釋的起始位置是否在字符串中。

由於上述原因,我們需要一個變量來記錄當前狀態——是否在字符串中。因此我使用 awk 來解決。

我的解決方案
# filename: strip_c_comment.awk   
# issue: awk -f scrip_c_comment.awk test.c   
BEGIN { FS="" }   
!(ignore_line && $NF == "//") && !ignore_line-- {   
     ignore_line = 0;   
     for(i = 1; i <= NF; i++) {   
         if (ignore_block) {   
             if ($i $(i+1) == "*/") {   
                 ignore_block = 0  
                 i++ # remove '*'   
             }   
             continue  
         }   
         if (!instr && $i $(i+1) == "/*") {   
             ignore_block = 1  
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章