在进行注释转换之前,我们首先要对两种注释方法进行简单的对比:
- C语言注释风格为 / * 注释内容 * /,从 / * 开始,到 * / 结束,中间全是它的注释内容,也就是说,C注释状态可以进行多行注释;
- C++注释风格为 // 注释内容,不同的是,C++只能进行单行注释。
那么要如何进行转换呢?我们画图来分析。
在读取一个.c文件时,可能会遇到四种状态:无注释状态,即正常代码,C语言注释状态,C++注释状态和结束状态。
这四种状态之间的关系我们也画图来进行分析:
- 读文件时,正常代码直接写入,当遇到EOF直接到文件结束状态。遇到 / 时有两种情况,/ * 进入C注释状态,/ / 进入C++注释状态。在C注释状态下,遇到 * / 返回正常代码,在C++注释状态下,遇到 \n 返回正常代码,遇到EOF直接到文件结束状态。需要注意的是,由于C注释是多行注释,所以它不可能直接到文件结束状态。
下面我们写一个示例代码:
input.c
// 1.一般情况
int num = 0;
/* 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*/
接下来,我们采用多文件编译的方式,写函数来实现它。
看代码:
test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "CommentConvert.h"
void test()
{
FILE *pfRead = NULL;
FILE *pfWrite = NULL;
pfRead = fopen("input.c", "r");
if(pfRead == NULL)
{
perror("open file for read");
exit(EXIT_FAILURE);
}
pfWrite = fopen("output.c", "w");
if(pfWrite == NULL)
{
perror("open file for write");
exit(EXIT_FAILURE);
}
CommentConvert(pfRead, pfWrite);
fclose(pfRead);
pfRead = NULL;
fclose(pfWrite);
pfWrite = NULL;
}
int main()
{
test();
return 0;
}
CommentConvert.h
#ifndef __COMMENT_CONVERT_H__
#define __COMMENT_CONVERT_H__
#include <stdio.h>
#include <stdlib.h>
typedef enum State //定义一个枚举,里面是四种状态
{
NUL_STATE, //无注释状态(正常代码)
C_STATE, //c注释状态
CPP_STATE, //c++注释状态
END_STATE, //结束状态
}State;
void CommentConvert(FILE *pfRead, FILE *pfWrite);
void DO_NULSTATE(FILE *pfRead, FILE *pfWrite, State *ps);
void DO_CSTATE(FILE *pfRead, FILE *pfWrite, State *ps);
void DO_CPPSTATE(FILE *pfRead, FILE *pfWrite, State *ps);
#endif
CommentConvert.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "CommentConvert.h"
void CommentConvert(FILE *pfRead, FILE *pfWrite)
{
//注释转换
State state = NUL_STATE; //定义初始状态为无注释状态
while(state != END_STATE)
{
switch(state)
{
case NUL_STATE:
DO_NULSTATE(pfRead, pfWrite, &state);
break;
case C_STATE:
DO_CSTATE(pfRead, pfWrite, &state);
break;
case CPP_STATE:
DO_CPPSTATE(pfRead, pfWrite, &state);
break;
default:
break;
}
}
}
void DO_NULSTATE(FILE *pfRead, FILE *pfWrite, State *ps)
{
int first = fgetc(pfRead);
switch(first)
{
case '/':
{
int second = fgetc(pfRead);
switch(second)
{
case '*': //C注释状态(/*)
{
fputc('/', pfWrite);
fputc('/', pfWrite);
*ps = C_STATE;
}
break;
case '/': //C++注释状态(//)
{
fputc(first, pfWrite);
fputc(second, pfWrite);
*ps = CPP_STATE;
}
break;
default:
{
fputc(first, pfWrite);
fputc(second, pfWrite);
}
break;
}
}
break;
case EOF: //文件结束的标志
fputc(first, pfWrite);
*ps = END_STATE;
break;
default: //正常代码
fputc(first, pfWrite);
break;
}
}
void DO_CSTATE(FILE *pfRead, FILE *pfWrite, State *ps)
{
int first = fgetc(pfRead);
switch(first)
{
case '*':
{
int second = fgetc(pfRead);
switch(second)
{
case '/':
{
int third = fgetc(pfRead);
if(third == '\n') //当*/后面没有内容时,将\n写入文件
{
fputc(third, pfWrite);
}
else //当*/后面还有内容时,比如/* */ /* */或/* */ int a = 1;必须先写入\n,再把读到的内容还回去,防止它被丢掉。
{
fputc('\n', pfWrite);
ungetc(third, pfRead);
}
*ps = NUL_STATE;
}
break;
case '*': //处理**/ ****/这种情况
{
fputc(first, pfWrite);
ungetc(second, pfRead);
}
break;
default: //正常代码
{
fputc(first, pfWrite);
ungetc(second, pfRead);
}
break;
}
}
break;
case '\n': //c注释是多行注释,在转换成c++注释时应先写入\n,再写入//
{
fputc(first, pfWrite);
fputc('/', pfWrite);
fputc('/', pfWrite);
}
break;
default: //正常代码
fputc(first, pfWrite);
break;
}
}
void DO_CPPSTATE(FILE *pfRead, FILE *pfWrite, State *ps)
{
int first = fgetc(pfRead);
switch(first)
{
case '\n': //C++注释一行,遇\n注释结束
{
fputc(first, pfWrite);
*ps = NUL_STATE;
}
break;
case EOF: //文件结束
*ps = END_STATE;
break;
default: //正常代码
fputc(first, pfWrite);
break;
}
}
测试结果: