题目:
编写一个删除c语言程序中所有的注释语句。要正确的处理带引号的字符串与字符常量。在c语言中注释不允许嵌套
首先需要理解什么是注释嵌套,c语言中有两种注释符,一种是行注释符//,一种是块注释符/**/
其中行注释符,一次只能注释一行,//之后的全部为注释
块注释符要成对出现,注释从/*开始到*/结束。
注释嵌套是指对块注释符所说,例如下面为注释嵌套的例子:
/* /* this is the comments */ */
注释从第一个/*开始,到第一个*/结束。所以最后一个*/不在注释范围内,这种注释是不被允许的,否则会导致编译错误
自我解答:
#include <stdio.h>
#define IN 1
#define OUT 0
int main()
{
int c;
bool comInStr = OUT; //the comment symbol // or /* in the string
bool blockCom = OUT; // the character is in comment block "/**/"
bool lineCom = OUT; // the character is in a comment line "//"
bool isComSymbol = false; // if it is comment symbol "//" or "/*"
bool outComBlock = false; //the flag for out of comment block
while((c = getchar()) != EOF)
{
if(comInStr)
{
putchar(c); //output the content in " "
if(c == '"')
comInStr = OUT; //end of the content in " "
}
else
{
if(lineCom == IN)
{
if(c == '\n') // will not output until to the end of the line
{
putchar(c);
lineCom = OUT;
isComSymbol = false;
}
}
else if(blockCom == IN)
{
if(outComBlock)
{
if(c == '/')
{
blockCom = OUT; //find the end symbol "*/"
isComSymbol = false;
}
outComBlock = false;
}
else
{
if(c == '*')
outComBlock = true;
}
}
else
{
if(c == '"')
{
comInStr = IN; //found the string
putchar(c);
continue;
}
if(isComSymbol)
{
if(c == '/')
lineCom = IN;
else if(c == '*')
blockCom = IN;
else
{
putchar('/');
putchar(c);
isComSymbol = false;
}
}
else
{
if(c == '/')
isComSymbol = true;
else
putchar(c);
}
}
}
}
return 0;
}
编程思路:定义布尔型变量lineCom和blockCom 分别表示当前是否在注释行和注释块之内。
顶层逻辑为:如果当前字符在注释行之内,则不输出字符,直至到行尾;如果当前字符在注释块之内,则不输出字符,直至遇到注释块结束符*/。通过识别“//”和“/*”来区分是否是注释行或注释块,在识别过程中需要忽略这两种字符在字符串常量之内出现
为上述程序写一个测试输入,尽可能包含各种情况:
#include <stdio.h> //test line comment
//test line comment
/*
test block comment
define the line comment and
block comment
*/
#define LINECOMMENT "//" /* test // in string */
#define BLOCKCOMMENT "/*" // test /* in string
int main()
{
int a = 0; /*test comment between two statements*/ int b = 1;
char str[] = "I am //, who are you"; //
char str1[] = "I am /*, and you"; // "haha I am here" /* */ */
return 0;
}
输出如下:
#include <stdio.h>
#define LINECOMMENT "//"
#define BLOCKCOMMENT "/*"
int main()
{
int a = 0; int b = 1;
char str[] = "I am //, who are you";
char str1[] = "I am /*, and you";
return 0;
}
从输出中可见,删除了注释部分并且保留了 字符串和字符常量中的 注释符号
参考答案:
#include <stdio.h>
void rcomment(int c);
void in_comment(void);
void echo_quote(int c);
/* remove all comments from a valid C program */
int main()
{
int c, d;
while((c = getchar()) != EOF)
rcomment(c);
return 0;
}
/* rcomment: read each character, remove the comments */
void rcomment(int c)
{
int d;
if(c == '/')
if((d = getchar()) == '*')
in_comment(); /* beginning comment */
else if(d == '/')
{
putchar(c); /* another slash */
rcomment(d);
}
else
{
putchar(c); /* not a comment */
putchar(d);
}
else if(c == '\'' || c == '"')
echo_quote(c); /* quote begins */
else
putchar(c); /* not a comment */
}
/* in_comment: inside of a valid comment */
void in_comment(void)
{
int c, d;
c = getchar(); /* prev character */
d = getchar(); /* curr character */
while(c != '*' || d != '/') /* search for end */
{
c = d;
d = getchar();
}
}
/* echo_quote: echo character within quotes */
void echo_quote(int c)
{
int d;
putchar(c);
while((d = getchar()) != c)
{ /* search for end */
putchar(d);
if(d == '\\')
putchar(getchar()); /* ignore escape seq */
}
putchar(d);
}
这个程序假设输入是一个合法的C程序。函数rcomment搜索注释语句的起始标志(/*);在找到这个标志时,它将调用另一个函数in_comment搜索注释语句的结束标志(*/),从而确保C程序中的注释语句都能被删除。
函数rcomment还将搜索单引号和双引号;在找到它们时,它将调用另一个函数echo_quote。函数echo_quote的参数将指明找到的字符是一个单引号还是一个双引号。echo_quote确保引号中的内容能够按原样输出,不会被误认为是注释。函数echo_quote不会把跟在一个反斜杠后面的引号看做是结束引号(参见教材第13页和练习1-2中关于转义字符序列的讨论)。其他任何字符都将按原样输出。
本程序将在getchar返回文件结束符时结束运行。
补充:
参考答案中的思路比较清晰,可读性较强。但此程序也存在一些问题:
首先参考答案程序的前提必须是一个合法的C程序。
例如下面的输入:int main() /*main function
被认为是一个不合法的程序,如果用这种输入,程序将会在in_comment中陷入死循环。而自我解答对这种输入并不敏感,当用同样的输入时,输出为:int main() 依然能把/*后面的注释去掉。
再例如下面的输入:#define LINECOMMENT "//,程序将在echo_quote中陷入死循环。自我解答依然可以正常输出。
其次参考答案程序只能处理块注释符即/**/的情况,对于行注释//不起作用。
例如用下面的输入int main() //main function
程序不能识别//而原样打印出来,自我解答可以处理这种情况
从参考答案中可以借鉴的地方有两个:一是在rcomment里面的递归调用,一个是对转义字符的处理。
自我解答中没有进行转义字符处理和单引号的处理,没有增加对单引号的处理是因为当时考虑,单引号定义的是单个字符不会出现注释符的情况。但是单引号内仍然可能会有转义字符的情况例如:'\''.在自我解答的程序进行这个功能的扩展也比较简单,如下即可:
int c, lastc; 增加lastc用于记录上次字符是单引号还是双引号
if(comInStr)
{
putchar(c);
if(c == '\\') //如果是转义字符打印下一个字符
{
putchar(getchar()); //print the next char
continue;
}
if(c == lastc) // 判断引号类型是否一致
comInStr = OUT; //end of the content in " "
}
在line61行增加 lastc = c; // store ' or " in lastc 记录当前引号类型