删除源文件注释语句----有限状态机算法实现

删除源文件注释语句的算法实现

面试某互联网公司,被问到“如何删除源文件中的注释语句?”,写了个很拙劣的算法,还没能考虑到“双引号中的//不用删除”这一环节。
写完后,面试官即说:“为什么不考虑用状态转换机呢?”
----状态转换机?什么鬼?怎么用?
书到用时方恨少啊!
回去后赶紧网上搜索“状态转换机”、“删除注释语句”,找到一个算法实现,感觉不是100%正确,但给了自己灵感。过几天又看了《C程序设计语言·第2版》,才发现原来练习题1-23正是当时的面试题啊!!(~书到用时方恨少~~~~~~~) 看了配套的习题解答,Clovis L. Tondo 和 Scott E. Gimpel 两位作者是用一个mian函数加三个辅助函数并使用递归技术实现的,个人感觉比较低效,但又给了自己灵感。于是自己决定用有限状态机来实现这个算法。
现在纸上画出了状态转换图,然后敲出了代码,实现了功能。后来想到C语言中还有“单行注释行继续符”,就是在单行注释的末尾使用一个反斜杠来表明下一行也同样属于注释语句,而自己的状态转换图和算法并没有实现,便又修改状态转换图并算法以实现之。今记录于此。

定义 (int ch,prevCh;) 两个变量,ch 为每次输入的字符的ASCII值。

定义 状态0~状态7 共8个状态:

状态0

初始状态
若ch=='/',可能为单行注释或多行注释,转状态1;
若ch=='\'' or '"',则为字符或字符串,用 prevCh=ch 记录下当前字符,转状态6;
其它情况,putchar(ch)输出字符,状态不变。

状态1

单行或多行注释半状态
若ch=='/',则确定为单行注释{//},转状态2;
若ch=='*',则确定为多行注释{/*},转状态4;
其它情况,putchar('/')输出之前的 '/',putchar(ch)输出当前的字符,转状态0。

状态2

单行注释
若ch=='\n',则单行注释结束,putchar('\n')输出换行符,转状态0;
若ch=='\\',是单行注释行继续符,属于情况{//xyz\},转状态3;
其它情况,状态不变。

状态3

单行注释行继续符开始
若ch=='\\',仍为单行注释行继续符,属于状态{//xyz\\},状态不变;
其它情况,属于状态{//xyz\a} 或 {//xyz\\n},转状态2。

状态4

多行注释开始
若ch=='*',属于情况{/*xyz*}或{/**},转状态5;
其它情况,属于情况{/*x}或{/*xyz*ab},状态不变。

状态5

多行注释可能结束
若ch=='/',则多行注释结束,转状态0;
若ch=='*',属于情况{/*xyz**},状态不变;
其它情况,属于情况{/*xyz*a},转状态4。

状态6

字符或字符串状态
若ch=='\\',为转义字符,属于状态{'\}或{"\},putchar(ch)输出当前字符'\\',转状态7;
若ch==prevCh,说明字符或字符串状态结束,putchar(ch)输出当前字符,转状态0;
其它情况,putchar(ch)输出当前字符,状态不变。

状态7

转义字符后面的字符
任意情况,putchar(ch)输出当前字符,转状态6。

状态转换图如下所示:

算法实现如下:

#include<stdio.h>

int main(int argc, char* argv[])
{
	int state = 0;//初始状态
	int  prevCh, ch;//ch为当前读取的字符,prevCh为上一个字符
	while ((ch = getchar()) != EOF) {
		switch (state) {
		case 0:
			if (ch == '/') {
				state = 1;
			}
			else if (ch == '\'' || ch == '"') {
				putchar(ch);
				prevCh = ch;
				state = 6;
			}
			else {
				putchar(ch);
			}
			break;
		case 1:
			if (ch == '/') {
				state = 2;
			}
			else if (ch == '*') {
				state = 4;
			}
			else {
				putchar('/');
				putchar(ch);
				state = 0;
			}
			break;
		case 2:
			if (ch == '\\') {
				state = 3;
			}
			else if (ch == '\n') {
				putchar(ch);
				state = 0;
			}
			break;
		case 3:
			if (ch != '\\') {
				state = 2;
			}
			break;
		case 4:
			if (ch == '*') {
				state = 5;
			}
			break;
		case 5:
			if (ch == '/') {
				state = 0;
			}
			else if (ch != '*') {
				state = 4;
			}
			break;
		case 6:
			if (ch == '\\') {
				putchar(ch);
				state = 7;
			}
			else if (ch == prevCh) {
				putchar(ch);
				state = 0;
			}
			else {
				putchar(ch);
			}
			break;
		case 7:
			putchar(ch);
			state = 6;
			break;
		default:
			putchar(ch);
			break;
		}
	}
	return 0;
}
在Windows上编译成 deleteComment.exe 文件之后,使用技巧是在命令行上将标准输入重定向到 .c 源文件,将标准输出重定向到目标文件,这样 getchar() 就会从源文件读取字符,而 putchar() 就会把字符输出到目标文件。
命令行用法如下:

E:\Code> deleteComment.exe < source.c > target.c

效果很好。

posted @ 2016-10-14 11:49  HorseShoe2016  阅读(966)  评论(0编辑  收藏  举报