怎么删除C/C++代码中的所有注释?浅谈状态机的编程思想

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

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

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

 

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

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

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

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

 

【一个简单的状态机】

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

 

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

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

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

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

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

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

这样,每当状态为IN的时候,意味着发现了一个单词,便可以输出当前字符;

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

 

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

在遍历输入字符流的时候,上面程序(机器)就只能处于两种状态,对应不同状态或状态切换可以做相应的处理。这样的程序不妨称为“状态机”。

 

按时上述思路,写代码就很容易了——

怎么删除C/C++代码中的所有注释?浅谈状态机的编程思想
#include <stdio.h>
#define OUT 0 /* outside a word */
#define IN 1  /* inside a word  */


int main(void)
{
    int c, state;

    state = OUT;
    while ( ( c = getchar() ) != EOF ) {
        if (c ==   || c == \n || c == \t) {
            if (state == IN)
                putchar(\n);
            state = OUT;
        } else {
            putchar(c);
            state = IN;
        }
    }
    return 0;
}
怎么删除C/C++代码中的所有注释?浅谈状态机的编程思想

 

让我们回到主题——

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

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

01)设正常状态为0

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

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

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

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

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

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

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

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

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

11)状态0中遇到‘,说明进入字符常量中,则进入状态5           ex. char a = ‘

12)状态5中遇到\,说明遇到转义字符,则进入状态6           ex. char a = ‘\

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

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

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

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

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

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

 

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

怎么删除C/C++代码中的所有注释?浅谈状态机的编程思想

 

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

怎么删除C/C++代码中的所有注释?浅谈状态机的编程思想
 1 /*
 2  *Copyright (C) Zhang Haiba
 3  *Date 2014-02-26
 4  *File exercise1_23.c
 5  *
 6  *this program removes all comments in grammatical C code,
 7  *and as a integrity solution for exercise1-23 in <<K&R>>
 8  */
 9 
10 #include <stdio.h>
11 #define debug
12 //#define debug(fmt, args...) fprintf(stderr, fmt, ##args)
13 
14 void dfa();
15 
16 int main(void)
17 {
18     dfa();
19     return 0;
20 }
21 
22 void dfa()
23 {
24     int c, state;
25 
26     state = 0;
27     while ((c = getchar()) != EOF) {
28         if (state == 0 && c == /)         // ex. [/]
29             state = 1;
30         else if (state == 1 && c == *)     // ex. [/*]
31             state = 2;
32         else if (state == 1 && c == /)    // ex. [//]
33             state = 4;
34         else if (state == 1)                // ex. [<secure/_stdio.h> or 5/3]
35             state = 0;
36 
37         else if (state == 2 && c == *)    // ex. [/*he*]
38             state = 3;
39         else if (state == 2)                // ex. [/*heh]
40             state = 2;
41 
42         else if (state == 3 && c == /)    // ex. [/*heh*/]
43             state = 0;
44         else if (state == 3)                // ex. [/*heh*e]
45             state = 2;    
46 
47         else if (state == 4 && c == \n)    // ex. [//hehe<enter>]
48             state = 0;
49 
50         else if (state == 0 && c == \‘)     // ex. [‘]
51             state = 5;
52         else if (state == 5 && c == \\)     // ex. [‘\]
53             state = 6;
54         else if (state == 6)                // ex. [‘\n or ‘\‘ or ‘\t etc.]
55             state = 5;
56         else if (state == 5 && c == \‘)    // ex. [‘\n‘ or ‘\‘‘ or ‘\t‘ ect.]
57             state = 0;
58 
59         else if (state == 0 && c == \")    // ex. ["]
60             state = 7;
61         else if (state == 7 && c == \\)     // ex. ["\]
62             state = 8;
63         else if (state == 8)                // ex. ["\n or "\" or "\t ect.]
64             state = 7;
65         else if (state == 7 && c == \")    // ex. ["\n" or "\"" or "\t" ect.]
66             state = 0;
67 
68         //debug("c = %c, state = %d\n", c, state);
69 
70         if ((state == 0 && c != /) || 
71             (state == 1 && c != * && c != /) ||
72             state == 5 || state == 6 || state == 7 || state == 8)
73             putchar(c);
74     }
75 }
怎么删除C/C++代码中的所有注释?浅谈状态机的编程思想

 

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

test.c如下:

怎么删除C/C++代码中的所有注释?浅谈状态机的编程思想
/*
 *This code make no sense,
 *but for exercise1_23 in <<K&R>> to test remove all comments in C code.
 */

#                include         <stdio.h>
#   include  <secure/_stdio.h>
#include <string.h>
# include      "/Users/apple/blog/zhanghaiba/KandR/test.h"
#define debug
# define LESS(i) ( ((i) << 31) / 2 )
#  define STRING "\"string\"" //to ensure legal
#define CHAR ‘\‘‘

int main(void)
{
    #ifdef A 
    "hehe..."
    #else     /*hehe*/
    "blala"
    #endif

    ///*testing*/
    int idx;
    if (idx > 3 && idx < 6) idx = idx/100;

    /* // */    
    char a = /;    // /
    char b = *;    // *
    char c = \‘;    //
    char d = \n;    // enter
    char e = \";    // "    
    char f = \\;    // \
    char g = <;    // <
    char h = >;    // >
    char i = ";    // "

    /* special***string */
    char special0[] = "\"hello, world!\"";
    char special1[] = "//";
    char special2[] = "he\"/*hehe*/";
    char *special = " \‘ hi \0\b\t \\\\ \a\e\f\n\r\v wolegequ \\ ";
    return        0;
}
怎么删除C/C++代码中的所有注释?浅谈状态机的编程思想

测试截图对比如下——

怎么删除C/C++代码中的所有注释?浅谈状态机的编程思想

 

【测试用例(2)./a.out < ~/open_src_code/glibc-2.17/malloc/malloc.c > test2.c】

glibc-2.17源码中的的malloc.c包括空行和注释有5163行,经过上面去注释后代码变成3625行。

测试发现去注释成功。这里不可能贴对比代码了。

有兴趣的读者可自行测试。

欢迎提供测试不正确的用例代码。

 

@Author: 张海拔

@Update: 2014-2-26

@Link: http://www.cnblogs.com/zhanghaiba/p/3569928.html

怎么删除C/C++代码中的所有注释?浅谈状态机的编程思想,布布扣,bubuko.com

怎么删除C/C++代码中的所有注释?浅谈状态机的编程思想

上一篇:《Effective C++》读书笔记之四 Item 4. Make sure that objects are initialized before they're used


下一篇:静态链表StaticLinkList的C++实现代码与可用头文件下载