前言
编写代码时会有多种注释方式,但有的编辑环境不识别其他的注释方式,因此做个小项目来实现将注释方式统一的改为一种。本次注释转换这个小项目就是将所有的C注释全部转换为C++注释。
概述
要实现这个转换就要先理清实际过程中代码都有哪几种状态!所以先来列一个状态转换机来梳理一下什么情况是什么状态。
简单考虑有以下几种状态:
- 无状态:代码的正文部分
- C状态:注释方式为C注释
- C++状态:注释方式为C++注释
- END:结束状态
对于此状态我们可以定义枚举常量:
enum STATUS
{
NUL_STATUS, //无注释状态
C_STATUS, //C语言注释状态
CPP_STATUS, //c++注释状态
END_STATUS, //结束注释状态
STR_STATUS //字符串注释状态
};
* 为了更加清楚直观的了解他们之间的转换关系,用图示的方法来说明一下:*
在此来说一下转换机制:先创建一个状态机,在读取到文件时将初始状态先设为NUL_STATUS,然后通过函数从文件中按一个字符一个字符的方式来读取,当读到’/‘时,说明接下来读到的是C_STATUS状态,然后将状态机的状态改为C_STATUS状态,进入C状态后,接下来就是要判断是否读到’/’,如果读到就将状态机设置为 NUL_STATUS状态。当然在进去C状态后还要考虑其他很多情况,具体在代码中来看。当读到’//’时,则进入CPP_STATUS状态,这时将状态机设置为CPP_STAUSE状态,CPP注释是按照行来注释的,所以判断CPP状态结束的标志自然就是’\n’了。在了解了状态机的转换机制之后,接下来就分析需求:
我们要新建并打开input.c文件,对这个文件内部的文件进行读取,然后处理,否则直接提示用户有错误。 接下来,我们还要打开output.c文件,对处理以后的代码进行写入。
功能描述:
编写一个小项目将一个一个文件中的注释都转换成C++的注释风格
设计部分:
- 头文件模块:包括模块中需要引用的头文件定义,需要实现的主要函数的声明
- 头文件中主要函数模块的实现:各个函数部分实现的细节
- 测试函数:检验函数模块的功能
- 写入一个输入文件
- 运行结果记入一个输出文件中
项目展示:
参考代码:
CommentConvert.h
/***********************************************************
* 程序名称:C/C++注释准换 *
* 编译环境:VS2013 *
* 编制日期:2018.6.03 *
* -----by Hunter *
***********************************************************/
#ifndef __COMMENTCONVERT_H__
#define __COMMENTCONVERT_H__
#include <stdio.h>
#include <stdlib.h>
enum STATUS
{
NUL_STATUS, //无注释状态
C_STATUS, //C语言注释状态
CPP_STATUS, //c++注释状态
END_STATUS, //结束注释状态
STR_STATUS //字符串注释状态
};
void CommentConvert(FILE* pfIn, FILE* pfOut);
void DoNulStatus(FILE* pfIn, FILE* pfOut); //无注释状态
void DoCStatus(FILE* pfIn, FILE* pfOut); //C语言注释状态
void DoCppStatus(FILE* pfIn, FILE* pfOut ); //c++注释状态
void DoStrStatus(FILE* pfIn, FILE* pfOut); //字符串注释状态
#endif //__COMMENTCONVERT_H__
CommentConvert.c
#include "CommentConvert.h"
enum STATUS status = NUL_STATUS;
void CommentConvert(FILE *pfIn, FILE *pfOut)
{
while (status != END_STATUS)
{
switch (status)
{
case NUL_STATUS:
DoNulStatus(pfIn, pfOut);
break;
case C_STATUS:
DoCStatus(pfIn, pfOut);
break;
case CPP_STATUS:
DoCppStatus(pfIn, pfOut);
break;
case STR_STATUS:
DoStrStatus(pfIn, pfOut);
break;
case END_STATUS:
break;
}
}
}
//无注释状态
void DoNulStatus(FILE *pfIn, FILE *pfOut)
{
int first = 0;
int second = 0;
first = fgetc(pfIn);
switch (first)
{
case '/':
second = fgetc(pfIn);
if (second == '/')
{
fputc('/', pfOut);
fputc('/', pfOut);
status = CPP_STATUS; //在无状态下遇到//直接将//写进去,并进入cpp状态
}
else if (second == '*')
{
fputc(first, pfOut);
fputc('/', pfOut);
status = C_STATUS;//在无状态下遇到/*要将/*变成//写进去,并进入c状态
}
else
{
fputc(first, pfOut);
fputc(second, pfOut);//如果遇到其他字符,直接写进去,状态不改变。
}
break;
case EOF:
status = END_STATUS;//如果遇到EOF,进入结束状态
break;
case '"':
fputc(first, pfOut);
status = STR_STATUS;//如果遇到",进入字符串状态
break;
default:
fputc(first, pfOut);//其他情况的字符,直接写进去就行
break;
}
}
//字符串注释状态
void DoStrStatus(FILE *pfIn, FILE *pfOut)
{
int first = 0;
first = fgetc(pfIn);
switch (first)
{
case '"':
fputc(first, pfOut);
status = NUL_STATUS;//在字符串状态如果遇到后引号,将后引号写入,并进入无状态
break;
default:
fputc(first, pfOut);//其他情况的字符,直接写进去就行
break;
}
}
//C语言注释状态
void DoCStatus(FILE *pfIn, FILE *pfOut)
{
int first = 0;
int second = 0;
int third = 0;
first = fgetc(pfIn);
switch (first)
{
case '*':
second = fgetc(pfIn);
if (second == '/') // 遇到*/ 说明c的注释结束,切换到无状态
{
status = NUL_STATUS;
third = fgetc(pfIn);
if (third != '\n')
{
fputc('\n', pfOut); //如果/后边的字符不是回车,需要写进去回车,然后再把独到的字符放回
ungetc(third, pfIn);
}
else
{
ungetc(third, pfIn);//如果是回车,把回车放回
}
}
else
{
fputc(first, pfOut);
//如果遇到*,但是后边不是/,把*放进去,*后边的字符放回,比如,/***/,进入c状态以后,
//遇到*,后边的字符也是*,如果把两个*都写进去,后边就只剩下/,就不能找到c状态结束的标志,
//所以,需要把第二个字符原样写回
/*fputc(second, pfOut);*/
ungetc(second, pfIn); //放回
}
break;
case '\n':
fputc(first, pfOut);//如果需要回车,吧回车写入,在写入//,状态不变。
fputc('/', pfOut);
fputc('/', pfOut);
break;
default:
fputc(first, pfOut);
break;
}
}
//C++注释状态
void DoCppStatus(FILE *pfIn, FILE *pfOut)
{
int first = 0;
int second = 0;
first = fgetc(pfIn);
switch (first)
{
case '\n':
fputc(first, pfOut);//cpp状态,如果遇到回车,cpp状态结束,进入无状态
status = NUL_STATUS;
break;
default:
if (first == EOF)
{
status = END_STATUS;
}
else
{
fputc(first, pfOut);
}
break;
}
}
test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "CommentConvert.h"
void test()
{
FILE* pfIn = NULL;
FILE* pfOut = NULL;
pfIn = fopen("input.c","r");
if (NULL == pfIn)
{
perror("the file for read");
exit(EXIT_FAILURE);
}
pfOut = fopen("output.c", "w");
if (NULL == pfOut)
{
perror("the file for write");
fclose(pfIn);
exit(EXIT_FAILURE);
}
//注释转换
CommentConvert(pfIn,pfOut);
fclose(pfIn);
fclose(pfOut);
}
int main()
{
test();
system("pause");
return 0;
}
结语
- status是一个全局变量,曾学到,尽量不要使用全局变量,是因为它不安全,想改就能改,根据它的这个特性,我们可以做到状态转换。但是,定义全局变量,也不要得寸进尺(将全局变量定义在头文件中),这样全局变量在每个文件中都可以被使用,被改变,而我们只要在我们当前的文件中使用。
- 打开文件就关闭文件:如果读取打开失败,报告错误再关闭文件;如果程序顺利执行之后,也要关闭文件。
好好学习,天天编程