在一个大型工程当中,由于代码人员的多样性,使得代码的注释风格不同。然而在发布的版本中,注释风格应该保持一致,以便于其他人员在后期的使用和维护。由此,产生了如下的需求:
实现一个对C/C++语言程序源文件中注释转换的功能(在这里我们实现的是C++注释转换为标准C语言注释)
首先说明:
C语言的注释是以斜杠星开始到第一次遇到星斜杠的一个字符串。
C++的注释是以双斜杠开始,直到遇到一个回车为止的字符串。
注释转换的要求:
其他注意事项:
具体分析:
在读取源文件的时候,可能遇到几种情况;C注释风格(包含注释的嵌套),C++风格注释(包含注释的嵌套),字符中嵌套注释,无注释,结束等几种状态。
引入“状态机”的概念:
状态机简写为FSM(Finite State Machine)主要分为两大类。第一大类:若输出只和状态与关而与输入无关,则称为Moore状态机
状态机可归纳为4个要素:即状态、条件、动作、次态。这样的归纳,主要是出于对状态机的内在因果关系的考虑,“现态”和“条件”是因,“动作”和“次态”是果。
状态机是表示有限个状态以及在这些状态下的转移和动作等行为的数学模型。
由这个思路,我们可以画出如下模型(当然并不严谨)
分析一下这个转换图:
非注释风格称为无状态,C注释风格称为C状态,C++注释风格称为CPP状态,文件结束称为END状态。
箭头旁边的标注代表状态转换的条件。
理清了这个逻辑,就可以在各个状态之间切换了。
下面是我写的代码(input.c作为测试源代码,output.c作为输出文件。)
首先是测试文件 input.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*/
然后是头文件 commentcovert.h
#define _CRT_SECURE_NO_WARNINGS 1
#ifndef __COMMENT_CONVERT_H__
#define __COMMENT_CONVERT_H__
#include<stdio.h>
#include<stdlib.h>
typedef enum CONVERT_STATE
{
NULL_STATE,
C_STATE,
CPP_STATE,
END_STATE
}StateType;
#define INPUTFILENAME "input.c"
#define OUTPUTFILENAME "output.c"
void CommentCovert();
void ConvertWork(FILE* read, FILE* write);
void DoNULL_STATE(FILE* read, FILE* write);
void DoC_STATE(FILE* read, FILE* write);
void DoCPP_STATE(FILE* read, FILE* write);
#endif //__COMMENT_CONVERT_H__
接着是具体实现文件commentcovert.c:
#define _CRT_SECURE_NO_WARNINGS 1
#include"commentcovert.h"
StateType state = NULL_STATE;
void DoNULL_STATE(FILE* read, FILE* write)
{
int first = fgetc(read);
int second = 0;
switch (first)
{
case '/':
second = fgetc(read);
if (second == '*')
{
fputc(first, write);
fputc('/', write);
state = C_STATE;
}
else if (second == '/')
{
fputc(first, write);
fputc(second, write);
state = CPP_STATE;
}
else
{
fputc(first, write);
fputc(second, write);
}
break;
case EOF:
{
state = END_STATE;
break;
}
default:
{
fputc(first, write);
break;
}
}
}
void DoC_STATE(FILE* read, FILE* write)
{
int first = fgetc(read);
int second = 0;
switch (first)
{
case '*':
second = fgetc(read);
if (second == '/')
{
fputc('\n',write);
state = NULL_STATE;
}
else
{
fputc(first,write);
ungetc(second,read);
}
break;
case EOF:
fputc(first, write);
state = END_STATE;
break;
case '\n':
//state = NULL_STATE;
fputc(first,write);
fputc('/',write);
fputc('/',write);
break;
default:
{
fputc(first, write);
break;
}
}
}
void DoCPP_STATE(FILE* read, FILE* write)
{
int first = fgetc(read);
int second = 0;
switch (first)
{
case '\n':
fputc(first, write);
state = NULL_STATE;
break;
case EOF:
state = END_STATE;
break;
default:
{
fputc(first, write);
break;
}
}
}
void ConvertWork(FILE* read, FILE* write)
{
state = NULL_STATE;
while (state != END_STATE)
{
switch (state)
{
case NULL_STATE:
DoNULL_STATE(read, write);
break;
case C_STATE:
DoC_STATE(read, write);
break;
case CPP_STATE:
DoCPP_STATE(read, write);
break;
}
}
}
void CommentCovert()
{
FILE* pWrite = NULL;
FILE* pRead = fopen(INPUTFILENAME, "r");
if (pRead == NULL)
{
perror("open file for read");
exit(EXIT_FAILURE);
}
pWrite = fopen(OUTPUTFILENAME, "w");
if (pWrite == NULL)
{
fclose(pRead);
perror("open file for write");
exit(EXIT_FAILURE);
}
ConvertWork(pRead, pWrite);
fclose(pRead);
fclose(pWrite);
}
最后是测试文件 test.h:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
//#include"commentcovert.h"
void test()
{
CommentCovert();
}
int main()
{
test();
return 0;
}
程序执行后得到输出文件output.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*/
当然你也可以指定其他转换方式,例如:C++转换成C语言的注释风格;
同样的,你也可以指定其他测试文件,完成你要的准换。
这里就不测试了!