怎样删除C++代码中的注释——有限状态机思想的使用

来源:互联网 发布:音效软件哪个最好 编辑:程序博客网 时间:2024/06/08 03:03

K&R习题1-23中,要求“编写一个程序,删除C语言程序中所有的注释语句。要正确处理带引号的字符串与字符常量。在C语言中,注释不允许嵌套”

如果不考虑字符常量和字符串常量,问题确实很简单。只需要去掉//和/* */的注释。

考虑到字符常量'\''和字符串常量"he\"/*hehe*/",还有类似<secure/_stdio.h>的头文件路径符号以及表达式5/3中的除号/,以及情况就比较复杂了。

另外,还有单行注释//中用\进行折行注释的蛋疼情况(这个情况连很多编辑器高亮都没考虑到)。

我想,这种问题最适合用正则表达式来解析,perl之类的语言应当很好处理,问题是这里让你用C语言实现,但是C语言对正则表达式并没有显式的支持。

学过编译原理的应该知道,正则表达式对应三型文法,也就对应着一个有限状态自动机(可以用switch偏重算法来实现,或者用状态转换矩阵/表偏重数据结构来实现),

所以这里的问题其实是设计一个状态机,把输入的字符流扔进去跑就可以了。

那什么是状态机呢?K&R第一章怎么没有介绍呢?

【一个简单的状态机】

先看《K&R》第一章的一个简单习题1-12:"编写一个程序,以每行一个单词的形式打印其输入"

在这个题目之前,1.5.4节的单词计数示例中,其实K&R已经展示了一个非常简单的状态机。但没有提到这种编程思想

当然这个题目也可以状态机的思想来编程。

回到题目,我们设初始的状态state为OUT,表示当前字符不在单词中(不是单词的组成字符),如果当前字符在单词中(属于单词的一部分),则state设为IN。

显然字符只能处于上述两种状态之一,有了这2个状态,我们就可以借助状态来思考问题 ——

(1)当前状态为OUT:若当前字符是空白字符(空格、制表符、换行符),则维护当前状态仍为OUT;否则改变状态为IN。

(2)当前状态为IN:若遇到的当前字符是非空白字符,则维护当前状态为IN;否则改变状态为OUT。

处于不同的状态,根据题意可以给予相对应的动作——

每当状态为IN的时候,意味字符属于单词的一部分,输出当前字符;

而当状态从IN切换为OUT的时候,说明一个单词结束了,此时我们输出一个回车符;

状态为OUT什么也不输出

可以看出,借助自定义的状态,可以使编程思路更加清晰。

在遍历输入字符流的时候,程序(机器)就只能处于两种状态,对应不同状态或状态切换可以有相应的处理动作

这样的程序不妨称为“状态机”。

按照上面的思路,代码实现就非常简单了——

[cpp] view plain copy
  1. #include <stdio.h>  
  2. #define OUT 0 /* outside a word */  
  3. #define IN 1  /* inside a word  */  
  4. int main(void)  
  5. {  
  6.     int c, state;  
  7.     state = OUT;  
  8.     while ( ( c = getchar() ) != EOF ) {  
  9.         if (state == OUT) {  
  10.             if (c == ' ' || c == '\t' || c == '\n')  
  11.                 state = OUT;  
  12.             else {  
  13.                 state = IN;  
  14.                 putchar(c); //action  
  15.             }  
  16.         } else {  
  17.             if (c != ' ' && c != '\t' && c != '\n') {  
  18.                 state = IN;  
  19.                 putchar(c); //action  
  20.             } else {  
  21.                 putchar('\n');//action  
  22.                 state = OUT;  
  23.             }  
  24.         }  
  25.     }  
  26.     return 0;  
  27. }  

让我们回到主题吧——

【“编写一个程序,删除C语言程序中所有的注释语句。要正确处理带引号的字符串与字符常量。在C语言中,注释不允许嵌套”】

按照注释的各方面规则,我们来设计一个状态机——

00)设正常状态为0,并初始为正常状态

每遍历一个字符,就依次检查下列条件,若成立或全部检查完毕,则回到这里检查下一个字符

01)状态0中遇到/,说明可能会遇到注释,则进入状态1          ex. int a = b; /

02)状态1中遇到*,说明进入多行注释部分,则进入状态2         ex. int a= b; /*

03)状态1中遇到/,说明进入单行注释部分,则进入状态4         ex. int a = b; //

04)状态1中没有遇到*或/,说明/是路径符号或除号,则恢复状态0      ex. <secure/_stdio.h> or 5/3

05)状态2中遇到*,说明多行注释可能要结束,则进入状态3        ex. int a = b; /*heh*

06)状态2中不是遇到*,说明多行注释还在继续,则维持状态2       ex. int a = b; /*hehe

07)状态3中遇到/,说明多行注释要结束,则恢复状态0          ex. int a = b; /*hehe*/

08)状态3中遇到*,则恢复状态3                                     ex. int a = b; /***

09)状态3中不是遇到/或*,说明多行注释只是遇到*,恢复状态2              ex. int a = b; /*hehe*h

10)状态4中遇到\,说明可能进入折行注释部分,则进入状态5       ex. int a = b; //hehe\

11)状态5中遇到\,说明可能进入折行注释部分,则维护状态5       ex. int a = b; //hehe\\\

12)状态5中遇到其它字符,则说明进入了折行注释部分,则恢复状态4    ex. int a = b; // hehe\a or hehe\<enter>

13)状态4中遇到回车符\n,说明单行注释结束,则恢复状态0        ex. int a = b; //hehe<enter>

14)状态0中遇到',说明进入字符常量中,则进入状态6           ex. char a = '

15)状态6中遇到\,说明遇到转义字符,则进入状态7           ex. char a = '\

16)状态7中遇到任何字符,都恢复状态6                 ex. char a = '\n 还有如'\t', '\'', '\\' 等 主要是防止'\'',误以为结束

17)状态6中遇到',说明字符常量结束,则进入状态0            ex. char a = '\n'

18)状态0中遇到",说明进入字符串常量中,则进入状态8         ex. char s[] = "

19)状态8中遇到\,说明遇到转义字符,则进入状态9           ex. char s[] = "\

20)状态9中遇到任何字符,都恢复状态8                 ex. char s[] = "\n 主要是防止"\",误以为结束

21)状态8中遇到"字符,说明字符串常量结束,则恢复状态0        ex. char s[] = "\"hehe"

前面说过,不同状态可以有相应的动作。比如状态0、6、7、8、9都需要输出当前字符,再考虑一些特殊情况就可以了。

读者实现时可以借助debug宏定义,将测试语句输出到标准错误输出,需要时可以重定位到标准输出,即2>&1,然后通过重定向|到more进行查看。

上面的状态机涉及到了[0, 9]一共10种状态,对应的状态转换图(或者说状态机/自动机)如下:


了这些状态表示,编写代码就很容易了——

[cpp] view plain copy
  1. #include<stdio.h>  
  2. int main()  
  3. {  
  4.     char c;  
  5.     int state=0;  
  6.     freopen("1.in","r",stdin);  
  7.     freopen("1.out","w",stdout);  
  8.     while((c=getchar())!=EOF)  
  9.     {  
  10.         switch(state)  
  11.         {  
  12.         case 0:  
  13.             if(c=='/')// ex. [/]  
  14.                 state=1;  
  15.             else if(c=='\'')// ex. [']  
  16.                 state=6;  
  17.             else if(c=='\"')// ex. ["]  
  18.                 state=8;  
  19.             else  
  20.                 putchar(c);  
  21.             break;  
  22.         case 1:  
  23.             if(c=='*')// ex. [/*]  
  24.                 state=2;  
  25.             else if(c=='/')// ex. [//]  
  26.                 state=4;  
  27.             else  
  28.             { // ex. [<secure/_stdio.h> or 5/3]  
  29.                 putchar('/');  
  30.                 putchar(c);  
  31.                 state=0;  
  32.             }  
  33.             break;  
  34.         case 2:  
  35.             if(c=='*'// ex. [/*he*]  
  36.                 state=3;  
  37.             else // ex. [/*heh]  
  38.                 state=2;  
  39.             break;  
  40.         case 3:  
  41.             if(c=='/')// ex. [/*heh*/]  
  42.                 state=0;  
  43.             else if(c=='*')  
  44.                 state=3;//ex. [/***]注意这里,不加这个条件,*的个数是奇数的时候出错  
  45.             else// ex. [/*heh*e]  
  46.                 state=2;  
  47.             break;  
  48.         case 4:  
  49.             if(c=='\\')// ex. [//hehe\]  
  50.                 state=5;  
  51.             else if(c=='\n')// ex. [//hehe<enter>]  
  52.             {  
  53.                 state=0;  
  54.                 putchar(c);  
  55.             }  
  56.             break;  
  57.         case 5:  
  58.             if(c=='\\')// ex. [//hehe\\\\\]  
  59.                 state=5;  
  60.             else// ex. [//hehe\<enter> or //hehe\a]  
  61.                 state=4;  
  62.             break;  
  63.         case 6:  
  64.             if(c=='\\')// ex. ['\]  
  65.                 state=7;  
  66.             else if(c=='\'')// ex. ['\n' or '\'' or '\t' ect.]  
  67.             {  
  68.                 state=0;  
  69.                 putchar(c);  
  70.             }  
  71.             break;  
  72.         case 7:// ex. ['\n or '\' or '\t etc.]  
  73.             state=6;  
  74.             break;  
  75.         case 8:  
  76.             if(c=='\\')// ex. ["\]  
  77.                 state=9;  
  78.             else if(c=='\"')// ex. ["\n" or "\"" or "\t" ect.]  
  79.             {  
  80.                 state=0;  
  81.                 putchar(c);  
  82.             }  
  83.             break;  
  84.         case 9:// ex. ["\n or "\" or "\t ect.]  
  85.             state=8;  
  86.             break;  
  87.         }  
  88.         if(state==6||state==7||state==8||state==9)  
  89.             putchar(c);  
  90.     }  
  91.     return 0;  
  92. }  

【测试用例(1)a.out < test.c > test2.c】
test.c如下:

[cpp] view plain copy
  1. /* 
  2.  *This code make no sense(Compiled successfully),  
  3.  *but for exercise1_23 in <<K&R>> to test remove all comments in C code. 
  4.  */  
  5.   
  6. #          include         <stdio.h>  
  7. #  include  <secure/_stdio.h>  
  8. #include      "/Users/apple/blog/zhanghaiba/KandR/test.h"  
  9. #define CHAR '\'' /*/test/*/  
  10. #  define LESS(i) ( ((i) << 31) / 2 )  
  11. #        define STRING "\"string\"" //to ensure legal  
  12.   
  13. int main(void)  
  14. {  
  15.     int w; // \a  
  16.     int x;/*hehe*/  
  17.     double y; // \   
  18.     double z; // \b \\\\  
  19.     int none;  
  20.   
  21.     ///*testing*/  
  22.     int idx;  
  23.     if (idx > 3 && idx < 6) idx = idx/100; //go and \  
  24.     con_tinue\  
  25.     hehe  
  26.   
  27.     /* // */      
  28.     char a = '/';    // /  
  29.     char b = '*';    // *  
  30.     char c = '\'';    // '  
  31.     char d = '\n';    // enter  
  32.     char e = '\"';    // "      
  33.     char f = '\\';    // \  
  34.     char g = '<';    // <  
  35.     char h = '>';    // >  
  36.     char i = '"';    // "  
  37.   
  38.     /* special***string */  
  39.     char special0[] = "\"hello, world!\"";  
  40.     char special1[] = "//test";  
  41.     char special2[] = "he\"/*hehe*/";  
  42.     char *special = " \' hi \0\b\t \\\\ \a\e\f\n\r\v wolegequ \\ ";  
  43.     return        0;  
  44. }  

原创粉丝点击