您的位置:首页 > 编程语言 > C语言/C++

实现C++注释转换为标准C语言注释

2018-03-23 16:41 603 查看
题目描述:
        请编写注释转化程序,实现对一个C/C++语言程序源文件中注释的转换功能
具体需求:
        1>C++风格的注释//注释转换为标准C分风格/* */注释
        2>/*  */风格的注释保持原样

        3>所有的转换需要符合语法规则

        4>注释转换需要支持注释嵌套

注释转化要求:
        注释的嵌套情形很多,这里只是举例,你需要遵照C/C++语言的注释 规则来编写代码,我不会仅测试这里的例子。

        1、单行注释或没有嵌套,注释行直接转换,如:

        1>//123             ==》                /*123*/

        2>/*123*/                                  /*123*/  不变  

        3>/*123

            */                                            保持原样

        2、有嵌套的注释(一个注释中还有嵌套其他注释符号//,/**/)嵌套中多余的每个注释符号用两个空格代替。

        如单行:

        1>//123/*456*/            ==》       /*123  456*/

        2>//123//456               ==》       /*123  456*/

        3>//123*//*456            ==》       /*123    456*/

        如跨行:

        /*.............                                     /*.............  

        //.............                ==》             ................  

        //.............                                     ................  

        */                                                */  

注意:
        1、除以下两种情况的修改,源文件转换后不能有任何其他的修改:

        1>多余的注释符用空格代替

        2>//在注释开始替换为/*,行尾增加*/

        2、下面的3种情形无需转换

        1>/*123*//*456*/

        2>/*123*//*456

            */

        3>/*123

            *//*456

            */

        3、不需要考虑输入文件中不符合语法规则的注释

首先我们定义一个状态机进行分类,将其分为C语言注释状态、C++注释状态、字符串状态、空状态和结束状态五大类,初始状态为空状态。(考虑到字符串中不对代码有影响的注释不应该被转换,因此多定义了一个字符串类)
读到/*时进入C语言注释状态,读到*/时返回空状态
读到//时进入C++注释状态,读到\n或\r\n时返回空状态
读到"时,对后续字符进行判断,看是否再次出现",有则进入字符串状态,再访问完第二个"后返回空状态

读到EOF时进入结束状态,并结束掉程序
在这里,我们顺便提一下状态机的概念:状态机的概念: 
有限状态机简称状态机,是表示有限个状态以及在这些状态之间转移的行为的模型。有限状态机是闭环系统,可以用有限的状态处理无穷的状态。 
状态机就是一组状态,各个状态之间,依据一定的条件,(如输入一个 1 或者是 0)存在一定的转换,(从状态X转换到状态Y)它有 
一个起始状态和若干终结状态,从起始状态开始,根据输入的串转换状
4000
态,直到所有的输入的被状态机处理,看看最后停留的状态是否为终结状态,是的话就说这个串符合这个状态机规则,或者说被这个状态机接受!通常我们使用switch case语句来处理有限状态机
//定义状态
typedef enum
{
NO_COMMENT_STATE,
C_COMMENT_STATE,
CPP_COMMENT_STATE,
STRING_STATE,
END_STATE
}enum_state;

//定义状态机
typedef struct
{
FILE *inputfile;
FILE *outputfile;
enum_state ulstate;
}state_machine;具体状况如下图所示:



为了方便大家的阅读,我在这里先把我写的一些函数声明在这里列举一下#ifndef _CONVERTCOMMENT_H
#define _CONVERTCOMMENT_H

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>

FILE* Fopen(char *filename, char *mode);
void Fclose(FILE *fp);
char read_ch(FILE *fp);
void write_ch(char ch, FILE *fp);
void write_double_ch(char ch1, char ch2, FILE *fp);

void eventpro(char ch);
void eventpro_no(char ch);
void eventpro_c(char ch);
void eventpro_cpp(char ch);
void eventpro_str(char ch);
int convertcomment(FILE *inputfile, FILE *outputfile);

#endif
定义两个包裹函数,减少代码的冗余,提升阅读性FILE* Fopen(char *filename, char *mode)
{
FILE *fp = fopen(filename, mode);
if(NULL == fp)
{
printf("open %s fail.\n",filename);
exit(1);
}
return fp;
}
void Fclose(FILE *fp)
{
fclose(fp);
}对转换后的代码进行一个简单的封装(展示可以跳过这块,理解代码思路后可在回过头来看这)char read_ch(FILE *fp)
{
assert(NULL != fp);
return fgetc(fp);
}
void write_ch(char ch, FILE *fp)
{
assert(NULL != fp);
fputc(ch, fp);
}
void write_double_ch(char ch1, char ch2, FILE *fp)
{
assert(NULL != fp);
fputc(ch1, fp);
fputc(ch2, fp);
}定义两个状态,并对状态机进行初始化state_machine g_state;
state_machine g_pre_state;

int convertcomment(FILE *inputfile, FILE *outputfile)
{
if(NULL == inputfile || NULL == outputfile)
{
printf("argument is invalid.\n");
return -1;
}

//初始化状态机
g_state.inputfile = inputfile;
g_state.outputfile = outputfile;
g_state.ulstate = NO_COMMENT_STATE;

char ch;
while(END_STATE != g_state.ulstate)
{
ch = read_ch(g_state.inputfile);
eventpro(ch);
}
return 0;
}在这里定义一个状态切换函数void eventpro(char ch)
{
switch(g_state.ulstate)
{
case NO_COMMENT_STATE:
eventpro_no(ch);
break;
case C_COMMENT_STATE:
eventpro_c(ch);
break;
case CPP_COMMENT_STATE:
eventpro_cpp(ch);
break;
case STRING_STATE:
eventpro_str(ch);
break;
}
}
对初始状态的处理:void eventpro_no(char ch)
{
char nextch;
switch(ch)
{
case '/':
nextch = read_ch(g_state.inputfile);
if('/' == nextch) //C++ Comment
{
write_double_ch('/','*', g_state.outputfile);
g_state.ulstate = CPP_COMMENT_STATE;
}
else if('*' == nextch) //C Comment
{
write_double_ch('/','*', g_state.outputfile);
g_state.ulstate = C_COMMENT_STATE;
}
else
{
write_double_ch('/', nextch, g_state.outputfile);
}
break;
case '"':
write_ch('"', g_state.outputfile);
eventpro_str(ch);
break;
case EOF:
g_state.ulstate = END_STATE;
break;
default:
write_ch(ch, g_state.outputfile);
break;
}
}对C语言注释的处理:void eventpro_c(char ch)
{
char nextch;
switch(ch)
{
case '/':
nextch = read_ch(g_state.inputfile);
if('/' == nextch || '*' == nextch) //C++ Comment
{
write_double_ch(' ', ' ', g_state.outputfile);
}
else
{
write_double_ch('/', nextch, g_state.outputfile);
}
break;
case '*':
nextch = read_ch(g_state.inputfile);
if('/' == nextch)
{
write_double_ch('*', '/', g_state.outputfile);
g_state.ulstate = NO_COMMENT_STATE;
}
else
{
write_double_ch('*', nextch, g_state.outputfile);
}
break;
case '"':
eventpro_str(ch);
break;
case EOF:
g_state.ulstate = END_STATE;
break;
default:
write_ch(ch, g_state.outputfile);
break;
}
}对C++注释的处理:void eventpro_cpp(char ch)
{
char nextch;
switch(ch)
{
case '\n':
write_double_ch('*', '/', g_state.outputfile);
fputc('\n',g_state.outputfile);
g_state.ulstate = NO_COMMENT_STATE;
break;
case '/':
nextch = read_ch(g_state.inputfile);
if('/' == nextch || '*' == nextch)
{
write_double_ch(' ', ' ', g_state.outputfile);
}
else
{
write_double_ch('/', nextch, g_state.outputfile);
}
break;
case '*':
nextch = fgetc(g_state.inputfile);
if('/' == nextch)
{
write_double_ch(' ', ' ', g_state.outputfile);
}
else
{
write_double_ch('*', nextch, g_state.outputfile);
}
break;
case '"':
eventpro_str(ch);
break;
case EOF:
write_double_ch('*', '/', g_state.outputfile);
g_state.ulstate = END_STATE;
break;
default:
write_ch(ch, g_state.outputfile);
break;
}
}对字符串状态的处理:void eventpro_str(char ch)
{
char nextch;
int flag = 1;
int mark = 1;
long n = 1;
write_ch('"',g_state.outputfile);
nextch = read_ch(g_state.inputfile);
while(flag && mark && EOF != nextch)
{
if('"' == nextch)
{
flag = 0;
}
else if('\n' == nextch)
{
mark = 0;
}
else
{
nextch = read_ch(g_state.inputfile);
n++;
}
}
if(1 == flag && 1 == mark) //EOF情况
{
fseek(g_state.inputfile, -n+1, 1);
nextch = read_ch(g_state.inputfile);
while(EOF != nextch)
{
write_ch(nextch, g_state.outputfile);
nextch = read_ch(g_state.inputfile);
}
}
else if(1 == flag && 0 == mark) //'\n'情况
{
fseek(g_state.inputfile, -n-1, 1);
nextch = read_ch(g_state.inputfile);
while('\n' != nextch)
{
write_ch(nextch, g_state.outputfile);
nextch = read_ch(g_state.inputfile);
}
}
else //'"'情况
{
fseek(g_state.inputfile, -n, 1);
nextch = read_ch(g_state.inputfile);
while('"' != nextch)
{
write_ch(nextch, g_state.outputfile);
nextch = read_ch(g_state.inputfile);
}
write_ch('"', g_state.outputfile);
}
}以上代码在实现之后,自己也进行了很多苛刻的用例进行测试,发现了一些问题:

        1>在Windows系统里面,每行结尾是“<回车><换行>”,即“ \r\n”,而不像Linux中时以"\n"结尾的;
        2>当遇见////////////////////////////时,这种情况我不想产生太多的空格,仍旧打算用两个空格来替换,然后我发现,当代码进行6/2=3用例测试时,会转化为6 2=3;于是又对代码进行了完善,才使得用例通过
        3>当遇见"的时候,并不一定进入字符串状态,应该用fseek进行检测,如果检测到下一个"时,才意味着进入了字符状态。如果检测到\n、\r\n或EOF,则只能将"视为普通字符来看

以下是我用的一些测试用例:
//每个区由若干个内存块组成

//每个区由若干个内存块组成,//每个块是4096个字节

//int i = 0;*/              

//*//*int i = 0;            

// /**/int i = 0;           

/* int i = 0;               
 *//*
 */

/* int i = 0;
// */int j = 0;              

/*
//每个区由若干个内存块组成,每个块是4096个字节
//每个块的第0个整数指向下个区
//所以是单链表结构
//所以每个区只有4092个字节存放真正的数据                
*/

/* int i = 0;*//*int j = 0;               
 */

/*
*//*
 */int i =0;                             

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////     5

"abcdefghijklmn~~~~!!!!!!!!"

/////////////xyz

//6/2=3

char *str = "hello //comment convert.";

char *str1 = "hello /* comment */ convert.";

/*abc**/

//abc"xy//z"

//abc"xy*/z"

希望这篇文章能够帮助到你,可能还有某些特殊的用例个人没有找到,还希望大家能够提出,多多指教
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: