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  
 

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