问题描述
将所有c风格的注释转换为c++风格,其中具体情况如下所示
转换前
// 1.一般情况
/* int i = 0; */
// 2.换行问题
/* int i = 0; */int j = 0;
/* int i = 0; */
int j = 0;
// 3.匹配问题
/*int i = 0;/*xxxxx*/
// 4.多行注释问题
/*
int i=0;
int j = 0;
int k = 0;
*/int k = 0;
// 5.连续注释问题
/**//**/
// 6.连续的**/问题
/***/
// 7.C++注释问题
// /*xxxxxxxxxxxx*/
问题分析
/* int i = 0; */
// int i = 0;
如果碰到一个/ 就有可能是c注释 ,
设计一个标记为来标记到达/的状态
当碰到第二个*时就 确定是c风格的注释
设计一个标记为来标记到达/*的状态(c风格注释)
同时将 /* 变成//
然后 * 以后的就是c风格的注释
如果在此基础上又遇到* 这里可能的情况如下:
1.遇到的是 中间的*
/* * */
应该打印此时的*
2.是结尾的*
/* */
不应该打印此时的*
3. *后碰到其他字符
/* *5 */
此刻*5 又变成了c风格的注释,也就是要打印*5
对于这三种情况,似乎遇到 * 又是一种状态
那我们又要用一个标志位来记录到达了 *的状态
其实当第一次遇见 /时,就有
// 可能是c++注释状态
/ 5 可是是普通状态 ( 10 / 5)
/* c注释状态
其中 进入c注释状态又有很多状态
那么 我们就要设置很多的标记位
这些标记位也不利于后续的管理
有限状态机
有限状态机,(英语:Finite-state machine, FSM),又称有限状态自动机,简称状态机,是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。
我对有限状态机的理解是: 可以将一个事件的所有状态穷举出来,
而且每种状态的转换也是可以穷举出来的.
其实有个很直观的例子是vim的多种模式和切换
一说到模式,用vi/vim的童鞋就会想到 insert normal v等模式
这些其实也是有限状态的集合,而且每种模式的转换方式都可以列出来
其中vi/vim基本的状态有:命令模式,输入模式,末行模式(当然不全,只是举例)
vi/vim的相互转换也在图片中体现出来了,
这就是一个有限状态机的例子
回到我们的问题上来
我们发现这些标记位(状态)是有限的,
那么我们可以用枚举来将其管理起来
typedef enum state{
NORMAL,//普通模式
FOUND_SLASH,//进入 / 模式
CPP_COMMENT,// 进入c++注释模式
C_COMMENT,//进入c模式
FOUND_START,//进入c模式下的*模式
}state;
他们的相互转换如图所示:
有了状态转换图我们就可以很容易的写出
转换代码了,
我们首先应该把各种状态的转换代码写出来
然后在考虑 转换之后出现的事件( c注释变成c++注释)
状态转换可以使用swtich开关语句
具体转换如代码所示
int main()
{
while(1)
{
ch = fgetc(input);
if(ch == EOF)
{
break;
}
switch(s)
{
case NORMAL:
{
if(ch == '/')
{
s = FOUND_SLASH;
}
else
{
// keep Normal
}
break;
}
case FOUND_SLASH:
{
if(ch == '/')
{
// 进入 / 模式
s = CPP_COMMENT;
//c++
}
else if( ch == '*' )
{
//c
s = C_COMMENT;
}
break;
}
case CPP_COMMENT:
{
if(ch == '\n')
{
//back to normal
s = NORMAL;
}
else
{
//c++注释
fputc(ch, output);
}
break;
}
case C_COMMENT:
{
if(ch == '*')
{
s = FOUND_START;
}
//多行注释
else if(ch == '\n')
{
fprintf(output,"//");
}
else
{
fputc(ch,output);
}
break;
}
case FOUND_START:
{
if(ch == '/')
{
s = NORMAL;
}
else if(ch == '*')
{
s = FOUND_START;
fputc('*',output);
}
else
{
/** 5 */
//backt to C_COMMENT
s = C_COMMENT;
}
break;
}
}
}
}
转换注释
当我们完成了状态的转换,就要考虑在转换时处理注释问题
这里有一下注意几点
1. 当 普通状态进入/ 时 这个/肯定要打印 (c和c++注释都以/ 开头)
当 进入cpp模式,要再打印一个/ 变成//形式 (c++注释保持不变)
cpp模式结束标志是 遇到换行 \n 此时打印换行并且进入Normal模式
如果在 /模式遇到* 此时*不打印 并且打印 / ,(注释转换),并且进入C_COMENT模式
在C_COMENT模式如果遇到* 则进入*模式
- 进入*模式后如果再次碰到 / 表示结束注释此时什么都不打印,并且要考虑换行问题,在其后面加上 \n
/* int i = 0; */int j = 0;
// int i = 0;
// int j = 0;
- 如果进入模式后如果再次碰到, 就要把上一个*打印出来
/* * */
- 如果进入模式碰到了other(其他字符) 那么此字符和就是注释一部分,要将其打印出来
- 在C_COMMENT模式里面如果遇到换行,要在换行之后加上 //
/*
int i=0;
int j = 0;
int k = 0;
*/int k = 0;
// int i = 0;
// int j = 0;
// int k = 0;
int k = 0;
完整代码
#include<unistd.h>
#include<stdio.h>
typedef enum state{
NORMAL,
FOUND_SLASH,
CPP_COMMENT,
C_COMMENT,
FOUND_START,
}state;
int main()
{
FILE* input,*output;
input = fopen("./input.c","r");
output = fopen("./output.c","w");
char ch,nextch;
state s = NORMAL;
while(1)
{
ch = fgetc(input);
if(ch == EOF)
{
break;
}
switch(s)
{
case NORMAL:
{
if(ch == '/')
{
//开始转换
fputc('/',output);
s = FOUND_SLASH;
}
else
{
fputc(ch,output);
}
break;
}
case FOUND_SLASH:
{
if(ch == '/')
{
fputc('/',output);
s = CPP_COMMENT;
//c++
}
else if( ch == '*' )
{
fputc('/',output);
s = C_COMMENT;
}
break;
}
case CPP_COMMENT:
{
if(ch == '\n')
{
//back to normal
fputc('\n',output);
s = NORMAL;
}
else
{
//c++注释
fputc(ch, output);
}
break;
}
case C_COMMENT:
{
if(ch == '*')
{
s = FOUND_START;
}
//多行注释
else if(ch == '\n')
{
fputc('\n',output);
fprintf(output,"//");
}
else
{
fputc(ch,output);
}
break;
}
case FOUND_START:
{
// /* */
// //
if(ch == '/')
{
nextch = fgetc(input);
if(nextch != '\n')
fputc('\n',output);
ungetc(nextch,input);
s = NORMAL;
//注释结束换行
}
/* * */
// *
else if(ch == '*')
{
s = FOUND_START;
fputc('*',output);
}
else
{
/** 5 */
fputc('*',output);
fputc(ch,output);
s = C_COMMENT;
}
break;
}
}
}
}