删除源文件注释语句的算法实现
面试某互联网公司,被问到“如何删除源文件中的注释语句?”,写了个很拙劣的算法,还没能考虑到“双引号中的//不用删除”这一环节。
写完后,面试官即说:“为什么不考虑用状态转换机呢?” ----状态转换机?什么鬼?怎么用? 书到用时方恨少啊! 回去后赶紧网上搜索“状态转换机”、“删除注释语句”,找到一个算法实现,感觉不是100%正确,但给了自己灵感。过几天又看了《C程序设计语言·第2版》,才发现原来练习题1-23正是当时的面试题啊!!(~定义 (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。
状态转换图如下所示:
算法实现如下:
#includeint 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
效果很好。