漂亮的LinuxC注释转换器--(1)实现注释转换

本文介绍了一款用于统一C/C++代码注释风格的工具,能够将C++风格注释转换为C语言风格,并支持注释嵌套处理。文章详细描述了工具的功能、实现原理及有限状态机的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

源代码,经测试,可将世界乱码大赛作品成功转换
一.需求分析:
有时在一个系统中会出现不同风格的注释,有的使用C++注释方式// ,有的使用C语言注释方式/**/, 但有时在系统评审过点时,要求把所有的注释统一,若是C++工程,则使用//进行注释,若是C语言工程时,使用/* */注释,针对一个大工程而言,不可能纯手工进行修改,我们需要一个自动化的工具,因此注释转换工具应运而生。
二.功能描述:
(1)先实现C++风格的注释//转换成C语言风格的注释/**/
(2)/* */风格的注释保持原样
(3)所有的转换要符合语法规则
(4)注释转换要支持注释嵌套
//(5)具有批量处理文件的功能或者是目录级联功能转换
//(6)C++多态实现
//(7)C++与C注释风格的选择性转换

注意:带//本篇先不实现,随后加入
注释的嵌套情形很多,这里只是举例,你需要遵照C/C++语言的注释规则来编写代码,我不会仅测试这里的例子。
1、单行注释或没有嵌套,注释行直接转换,如:
     ①//123                             /* 123 *//* 123 */                       /* 123 */ 不变
     ③/*123
         */                                   保持原样
2、有嵌套的注释(一个注释中还有嵌套其他注释符号//,/* */)嵌套中多余的每个注释符号用两个空格代替。
如单行:
 ① //123 /*456 */                            /*123   456*/
 ②//123//456                                   /*123   456*/ 
 ③//123*//*456                               /*123     456*/
如跨行
    /*……..                                         /*……..
    //………                                        ……….
    // ……..                                         ……….
    */     
    1、除以下两种情况的修改,源文件转换后不能有任何其它的修改:
 ①多余的注释符用空格代替
 ②//在注释开始替换为/* ,行尾增加*/
2、下面的3种情形无需转换
 ① /* 123 */ /* 456 *//* 123 */ /* 456
    *//* 123
     */ /* 456
      */
3、不需要考虑输入文件中不符合语法规则的注释         

三.有限状态机的介绍:
1.概念术语:
关键词:事件驱动,有限状态机。

状态存储关于过去的信息,就是说:它反映从系统开始到现在时刻的输入变化。转移指示状态变更,并且用必须满足来确使转移发生的条件来描述它。动作是在给定时刻要进行的活动的描述。有多种类型的动作:
进入动作(entry action):在进入状态时进行
退出动作:在退出状态时进行
输入动作:依赖于当前状态和输入条件进行
转移动作:在进行特定转移时进行
下面展示最常见的表示:当前状态(B)和条件(Y)的组合指示出下一个状态(C)。完整的动作信息可以只使用脚注来增加。包括完整动作信息的 FSM 定义可以使用状态表。状态转移表
当前状态 →状态 A  状态 B    状态 C
条件 ↓    
条件 X    …   …   …
条件 Y    …   状态 C    …
条件 Z    …   …   …
除了建模这里介绍的反应系统之外,有限状态自动机在很多不同领域中是重要的,包括电子工程、 语言学、计算机科学、哲学、生物学、数学和逻辑学。有限状态机是在自动机理论和计算理论中研究的一类自动机。在计算机科学中,有限状态机被广泛用于建模应用行为、硬件电路系统设计、软件工程,编译器、网络协议、和计算与语言的研究。

2.作用:

它对数字系统的设计具有十分重要的作用。
有限状态机是指输出取决于过去输入部分和当前输入部分的时序逻辑电路。一般来说,除了输入部分和输出部分外,有限状态机还含有一组具有“记忆”功能的寄存器,这些寄存器的功能是记忆有限状态机的内部状态,它们常被称为状态寄存器。在有限状态机中,状态寄存器的的下一个状态不仅与输入信号有关,而且还与该寄存器的当前状态有关,因此有限状态机又可以认为是组合逻辑和寄存器逻辑的一种组合。其中,寄存器逻辑的功能是存储有限状态机的内部状态;而组合逻辑又可以分为次态逻辑和输出逻辑两部分,次态逻辑的功能是确定有限状态机的下一个状态,输出逻辑的功能是确定有限状态机的输出。

3.分类:

在实际的应用中,根据有限状态机是否使用输入信号,设计人员经常将其分为Moore型有限状态机和Mealy型有限状态机两种类型。1 Moore型有限状态机 其输出信号仅与当前状态有关,即可以把Moore型有限状态的输出看成是当前状态的函数。2 Mealy型有限状态机 其输出信号不仅与当前状态有关,而且还与所有的输入信号有关,即可以把Mealy型有限状态机的输出看成是当前状态和所有输入信号的函数。

4.编程:

有限状态机体现了两点:首先是离散的,然后是有限的。
State:
状态这个词有些难以定义,状态存储关于过去的信息,就是说它反映从系统开始到现在时刻的输入变化。
Actions & Transitions:
转换指示状态变更,并且用必须满足来确使转移发生的条件来描述它。动作是在给定时刻要进行的活动的描述。
Guards:
检测器出现的原因是为了检测是否满足从一个状态切换到另外一个状态的条件。
Event:
事件,又见事件,笼统说来,对系统重要的某件事情被称为事件。
恩,就这些了,有些迷惑么:),恩,我们来理清一下思路:先从事件说起,事件是有生命
的,它经历:
1).被产生(被接受,等待被处理,一般放入事件队列)
2).被分发(从事件队列取出,分发到响应的状态机处理)
3).死亡(当状态机处理了该事件,它随之死亡)
从一个状态切换到另外一个状态被称为状态转换,而引起它的事件称为触发事件.(可以看到,不是所有的事件都会引起状态的转换).
提到状态转换,不能不提及检测器(Guards),只有当检测器的值为TRUE时候,才能启动转换

5.应用:

常见的计算机就是使用有限状态机作为计算模型的:对于内存的不同状 态,CPU通过读取内存值进行计算,更新内存中的状态。CPU还通过消息总线接受外部输入设备(如键盘、鼠标)的指令,计算后更改内存中的状态,计算结果 输出到外部显示设备(如显示器),以及持久化存储在硬盘。
电脑游戏设计中也经常使用有限状态机模型。以水果忍者游戏为例,游戏中水 果的状态是有限状态,其运行轨迹是由模拟物理运动规律的计算公式运算而成的,一个香蕉抛起来后会按照抛物线运行,其每一帧位置变化都是一个状态的改变,状 态改变通过计算公式来决定。当然作为游戏不会仅仅这么简单,如果这么简单就是动画了,游戏还有复杂的人机交互事件,比如用手在屏幕上“切”了水果,水果感 知到这个事件后,会按照程序逻辑进入爆炸状态。

四.具体实现介绍:
1.框架搭建 input.c → ConComment-> output.c
2.具体实现思路图解:
这里写图片描述
(1)5个状态用结构体定义:

typedef enum
{
    NO_COMMENT_STATE,
    C_COMMENT_STATE,
    CPP_COMMENT_STATE,
    STR_COMMENT_STATE,
    END_COMMENT_STATE
}STATE_ENUM;

(2)状态机记录输入文件,输出文件,当前状态,之前状态

typedef struct
{
    FILE *inputfile;
    FILE *outputfile;
    STATE_ENUM state;
    STATE_ENUM prestate;        //记录先前的状态,进入字符串前进行记录
}State_Machine;

(3)利用函数模块针对各个状态进行操作:

void EventPro(char ch);              //事件处理
void EventProAtNo(char ch);          //无注释状态
void EventProAtC(char ch);           //C语言状态
void EventProAtCpp(char ch);         //Cpp状态
void EventProAtStr(char ch);         //字符串状态
//公共函数(读写操作)的声明
char readchr();                      //读取一个字符串
void Write_One_Data(char ch);        //用来写入一个字符
void Write_Data(char ch1, char ch2); //写入两个字符

(4)具体实现:

void EventPro(char ch)
{
    switch(g_state.state)
    {
    case NO_COMMENT_STATE:
        EventProAtNo(ch);
        break;
    case C_COMMENT_STATE:
        EventProAtC(ch);
        break;
    case CPP_COMMENT_STATE:
        EventProAtCpp(ch);
        break;
    case STR_COMMENT_STATE:
        EventProAtStr(ch);
        break;
    default:
        break;
    }
}

void EventProAtNo(char ch)
{
    char nextch;
    switch(ch)
    {
    case '\n':
            Write_One_Data('\n');
            break;
    case '/':
        nextch = readchr();
        if(nextch == '/')   //  //
        {
            Write_Data(ch, '*');
            g_state.state = CPP_COMMENT_STATE;
        }
        else if(nextch == '*') // /*
        {
            Write_Data(ch, nextch);
            g_state.state = C_COMMENT_STATE;
        }
        else
            Write_Data(ch, nextch);
        break;
    case '*':
        nextch = readchr();
        if(nextch == '/')
            Write_Data(' ', ' ');
        else
            Write_Data(ch, nextch);
        break;
    case '\"':
        Write_One_Data(ch);
        g_state.state = STR_COMMENT_STATE;
        g_state.prestate = NO_COMMENT_STATE;
        break;
    case EOF:
        g_state.state = END_COMMENT_STATE;
        break;
    default:
        Write_One_Data(ch);
        break;
    }
}
void EventProAtC(char ch)
{
    char nextch;
    switch(ch)
    {
    case '*':
        nextch = readchr();
        if(nextch == '/')
        {
            Write_Data('*', '/');
            g_state.state = NO_COMMENT_STATE;
            g_state.prestate = NO_COMMENT_STATE;
        }
        else if(nextch == '*')
        {
            Write_One_Data(ch);
            fseek(g_state.inputfile, -1L, SEEK_CUR);//奇数个*,最后一个设置成与/匹配的情况,eg./***/
        }
        else
            Write_Data(ch, nextch);
            break;
    case '\n':
        if(g_state.prestate == CPP_COMMENT_STATE)
        {
            Write_Data('*', '/');
            g_state.state = NO_COMMENT_STATE;
        }
        else
            Write_One_Data('\n');
        break;
    case '\"':
        Write_One_Data(ch);
        g_state.state = STR_COMMENT_STATE;
        g_state.prestate = C_COMMENT_STATE;
        break;
    case '/':
        nextch = readchr();
        if(nextch == '/')
        {
            Write_Data(' ', ' ');
        }
        else
        {
                Write_Data(ch, nextch);
        }
        break;
    case EOF:
        g_state.state = END_COMMENT_STATE;
        break;
    default:
        Write_One_Data(ch);
        break;
    }
}
void EventProAtCpp(char ch)
{
    char nextch;
    switch(ch)
    {
    case '\n':
        Write_Data('*', '/');
            Write_One_Data('\n');
            g_state.state = NO_COMMENT_STATE;
            g_state.prestate = NO_COMMENT_STATE;
        break;
    case '\"':
        Write_One_Data(ch);
        g_state.state = STR_COMMENT_STATE;
        g_state.prestate = CPP_COMMENT_STATE;
        break;
    case '/':
        nextch = readchr();
        if(nextch == '/')
        {
            Write_Data(' ', ' ');
        }
        else if(nextch == '*')
        {
            Write_Data(' ', ' ');
        }
        else
        {
            fseek(g_state.inputfile, -3L, SEEK_CUR);
            char bak_3 = readchr();
            if(bak_3 == '/')
                Write_One_Data(' ');
            else
                Write_One_Data(ch);
            fseek(g_state.inputfile, +1L, SEEK_CUR);
        }
        g_state.prestate = CPP_COMMENT_STATE;
        break;
    case '*':
        nextch = readchr();
        if(nextch == '/')
        {
            if(g_state.prestate == CPP_COMMENT_STATE)
            {
                Write_Data(' ', ' ');
            }
            else
            {
                Write_Data(ch, nextch);
                g_state.state = NO_COMMENT_STATE;
                g_state.prestate = CPP_COMMENT_STATE;
            }
        }
        else
            Write_Data(ch, nextch);
        break;
    case EOF:
        Write_Data('*', '/');
        g_state.state = END_COMMENT_STATE;
        break;
    default:
        Write_One_Data(ch);
        break;
    }
}
void EventProAtStr(char ch)
{
    char nextch;
    switch(ch)
    {
        case '\"':
            Write_One_Data(ch);
            g_state.state = g_state.prestate;
            break;
        case EOF:
            g_state.state = END_COMMENT_STATE;
            break;
        default:
            Write_One_Data(ch);
            break;
    }
}
char readchr()
{
     fgetc(g_state.inputfile);
}
void Write_One_Data(char ch)
{
    fputc(ch, g_state.outputfile);
}
void Write_Data(char ch1, char ch2)
{
    fputc(ch1, g_state.outputfile);
    fputc(ch2, g_state.outputfile);
}

五.收获:
(1)有限状态机
(2)标准I/O函数(fopen/fclose/fread/fwrite/fputc/fgetc)等的使用
此处附源代码链接<源代码请戳>
https://github.com/zhangzhuo233/little_project/tree/master/ConvertComment/day1

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值