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

题目描述:

        请编写注释转化程序,实现对一个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)它有 
一个起始状态和若干终结状态,从起始状态开始,根据输入的串转换状态,直到所有的输入的被状态机处理,看看最后停留的状态是否为终结状态,是的话就说这个串符合这个状态机规则,或者说被这个状态机接受!

通常我们使用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;

具体状况如下图所示:

实现C++注释转换为标准C语言注释_第1张图片

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

#ifndef _CONVERTCOMMENT_H
#define _CONVERTCOMMENT_H

#include
#include
#include

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"


希望这篇文章能够帮助到你,可能还有某些特殊的用例个人没有找到,还希望大家能够提出,多多指教

你可能感兴趣的:(项目)