每天进步一点点!
本题目要求我们编写一个可以将c风格(/**/)的注释转换为c++风格(//)的注释。
在代码注释时,我们可以使用/* ... */注释,中间为被注释的内容,这种注释方式允许我们一次性注释多行,还有一种:// ... ,这种注释一次最多只能注释一行,前者被称为为c风格的注释,后者被称为c++风格的注释。
我们通过状态机思路来完成注释转换,先解释一下状态机:通俗的将就是一组状态之间,依据一定条件,进行状态的转换,它有至少一个出口状态,即达到出口状态时,转换结束。
现将完整代码贴出,后面分析部分代码:
头文件
commentConvert.c
#ifndef __COMMET_CONVERT__
#define __COMMET_CONVERT__
#include <stdio.h>
#include <windows.h>
#include <assert.h>
#pragma warning(disable:4996)
#define INPUT_FILE "input.c"
#define OUTPUT_FILE "output.c"
//状态常量
typedef enum STATUS
{
NORMAL_STATUS,
C_STATUS,
CPP_STATUS,
END_STATUS,
}status_t;
extern status_t gStatus;
void convertBagin();
#endif
函数实现
commentConvert.c
#include "commentConvert.h"
status_t gStatus = NORMAL_STATUS;
//处理正常状态
static void doNormalStatus(FILE* in, FILE* out)
{
int first = fgetc(in);
int second = 0;
assert(in);
assert(out);
switch(first){
case '/' :
second = fgetc(in);
if( '*'== second){
fputc('/', out);
fputc('/', out);
gStatus = C_STATUS;
}else
if('/' == first){
fputc(first, out);
fputc(first, out);
gStatus = CPP_STATUS;
}
else{
fputc(first, out);
fputc(first, out);
}
break;
case EOF:
gStatus = END_STATUS;
break;
default:
fputc(first, out);
break;
}
}
//处理c状态
static void doCStatus(FILE* in, FILE* out)
{
int first = fgetc(in);
int second = 0;
int third = 0;
assert(in);
assert(out);
switch(first){
case '*' :
second = fgetc(in);
if('/' == second){
third = fgetc(in);
if('\n' == third){
fputc('\n', out);
}else{
fputc('\n', out);
ungetc(third, in);
}
gStatus = NORMAL_STATUS;
}
else{
fputc(first, out);
ungetc(second, in);
}
break;
case EOF :
gStatus = END_STATUS;
break;
case '\n':
fputc(first, out);
fputc('/', out);
fputc('/', out);
break;
default:
fputc(first, out);
break;
}
}
//处理cpp状态
static void doCppStatus(FILE* in, FILE* out)
{
int first = fgetc(in);
assert(in);
assert(out);
switch(first){
case '\n' :
fputc(first, out);
gStatus = NORMAL_STATUS;
break;
case EOF :
gStatus = END_STATUS;
break;
default:
fputc(first, out);
break;
}
}
//状态机
static void statusMachine(FILE* in, FILE* out)
{
assert(in);
assert(out);
while(END_STATUS != gStatus){//根据状态进入相应逻辑
switch(gStatus){
case NORMAL_STATUS:
doNormalStatus(in, out);
break;
case C_STATUS :
doCStatus(in, out);
break;
case CPP_STATUS :
doCppStatus(in, out);
break;
case END_STATUS :
break;
default:
break;
}
}
}
//注释转换开始
void convertBagin()
{
FILE* in = fopen(INPUT_FILE, "r");
FILE* out = fopen(OUTPUT_FILE, "w");//打开要转换的文件
if(NULL == in){
perror("fopen");
exit(1);
}
if(NULL == out){
perror("fopen");
exit(2);
}
statusMachine(in, out);//进入状态机开始转换
fclose(in);
fclose(out);//关闭文件
}
主函数
commentConvertMain.c
#include "commentConvert.h"
int main()
{
printf("Start convert...\n");
convertBagin();
printf("Done...\n");
system("pause");
return 0;
}
下面用状态机的思路来分析代码。
如图所示,只有触发图中几个条件时,状态机的状态才会发生改变,状态机出口条件为EOF,即读到文件结尾,所以,我们每次读取从输入文件中读取一个字符时,通过判断来改变状态,再进行相应操作,下面结合代码分析部分细节。
doCStatus分析:
由于是将c注释风格转换为c++风格,所以当读取到 /* 时需将其转换为 //,且当读取到 /* 时状态应变为cStatus。
在c状态下,由于其允许多行注释,所以该状态就有以下几种情况:
1、读到 '*' 并且读到 '/' ,后面为 '\n',此种情况下则直接转为正常状态,情况如下:
/*Hello world!*/
2、读到 '*' 并且读到 '/',但后面还有内容,则应该换行,情况如下:
/*Hello world*/int i = 10;
如果不进行换行,由于前面的 /* 已经转换为 // ,后面代码也会被注释掉;
3、读到文件结尾(EOF),则直接转为为EOF状态;
4、除上述之外的情况,均不作状态转换,直接将读到的字符写到output文件中。
其它情况均按照此思路处理。
转换前后的代码如下所示:
转换前:
/*hello world*/
/*hello bit
world
aaa
*/
/*hello bit*/ int a = 10;
//int main()
{
printf("hello world\n");
system("pause");
return 0;
//hello bit
/**/
}
//hello world
//hello bit
//world
//aaa
//
//hello bit
int a = 10;
//int main()
{
printf("hello world\n");
system("pause");
return 0;
//hello bit
//
}
成于坚持,败于止步!
【作者:果冻 http://blog.csdn.net/jelly_9】