目录
去除C程序中的注释
要求:
总时间限制: 1000ms
内存限制: 65536kB
描述:
C程序的注释用/*...*/来表示。请写一个程序,将输入的C程序源代码中的注释去掉,输出去掉注释之后的源代码。
用于测试的C代码保证符合语法,不使用C++的//注释语法。
注意,C语言不允许出现嵌套注释。具体来说,对于/*/**/"*/",如果不允许嵌套注释,那么它表示字符串"*/";如果允许嵌套注释,它表示一个引号"。
还请注意,字符串中出现的注释符/*属于字符串的一部分,注释中出现的双引号"属于注释的一部分。
输入:
符合语法的C代码文本文件。代码每行不超过200个字符。
输出:
去掉注释后的C代码。要求只能去掉注释,不可以做其他的修改,比如调整缩进,去除注释之外的换行符等。
样例输入:
#include #include #include /*Hash Search: Hash function: division method; handling collisions: open addressing's linear probing. In this exercise, M is the basic area's length, all keys are non negative integers.*/ #define M 11 int hash(int key) { return key % M; } void init_hash(int* hashtable) { int i; for(i = 0; i < M; ++i) { hashtable[i] = -1; } } /*return value: 1:found, *position is the key's index; 0:not found, *position is where to insert the key; -1:overflow. */ int search_hash(int* hashtable, int key, int* position) { int i, h = hash(key); for(i = 0; i < M; ++i) { if(key == hashtable[h]) { *position = h; return 1; } if(-1 == hashtable[h]) { *position = h; return 0; } h = (h+1) % M; } *position = -1; return -1; } /*return value: 1:inserted, 0:overflow*/ int insert_hash(int* hashtable, int key) { int position, result; result = search_hash(hashtable, key, &position); if(-1 == result) return 0; hashtable[position] = key; return 1; } void main() { int hashtable[M]; init_hash(hashtable); srand(time(NULL)); int i, j, key; for(i = 0; i < 8; ++i) /*make a hash table with 8 elements*/ { key = rand() % 50; insert_hash(hashtable, key); printf("Insert %d\n", key); for(j = 0; j < M; ++j) printf("%3d", hashtable[j]); printf("\n"); } printf("Please input the key to search:\n"); scanf("%d", &key); i = search_hash(hashtable, key, &j); if(1 == i) printf("Found!Its index is %d\n", j); else printf("Not found!\n"); }
样例输出:
#include #include #include #define M 11 int hash(int key) { return key % M; } void init_hash(int* hashtable) { int i; for(i = 0; i < M; ++i) { hashtable[i] = -1; } } int search_hash(int* hashtable, int key, int* position) { int i, h = hash(key); for(i = 0; i < M; ++i) { if(key == hashtable[h]) { *position = h; return 1; } if(-1 == hashtable[h]) { *position = h; return 0; } h = (h+1) % M; } *position = -1; return -1; } int insert_hash(int* hashtable, int key) { int position, result; result = search_hash(hashtable, key, &position); if(-1 == result) return 0; hashtable[position] = key; return 1; } void main() { int hashtable[M]; init_hash(hashtable); srand(time(NULL)); int i, j, key; for(i = 0; i < 8; ++i) { key = rand() % 50; insert_hash(hashtable, key); printf("Insert %d\n", key); for(j = 0; j < M; ++j) printf("%3d", hashtable[j]); printf("\n"); } printf("Please input the key to search:\n"); scanf("%d", &key); i = search_hash(hashtable, key, &j); if(1 == i) printf("Found!Its index is %d\n", j); else printf("Not found!\n"); }
思路分析:
这个题目我乍一看以为就是一道水题,只要注意一下引号的情况,讨论一下就可以,但是做着做着就发现问题很大,这个题目不是一般的麻烦,因为边界情况太多而样例太过普通.
仔细思考,我们需要讨论的情况很多:
首先,鉴于输入文件太大,我们最好的办法是用getchar()一个一个地读字符(注意⚠️getchar()比cin.get()更快),这样会遇到的第一个问题就是注释开始和注释结束的标志都是两个字符,我们怎么既处理两个字符的标志,又不影响到其他字符的讨论与判断.
对于注释开始/*标志,一个行之有效的方法是在读到‘/’的时候利用cin.peek()来看一下缓冲区下一个字符是什么(实际并没有取出),如果是‘*’的话,就可以设立一个标志(anno_tag)代表接下来是注释内容,不用输出.
对于注释结束*/标志,最好还是在getchar()读到'/'时再处理,因为这样的话之前的'*'就已经保证没有输出出来,为了实现这一步我们需要设置一个last_c来保存上一个读过的字符,如果是'*'就将注释标志设为false.
这样关于注释符号的处理看似结束了,但实际上还有一个隐藏的问题:对于/*/*/这样的语句,正确的结果是将整体看作一个注释,注释内容是'/',但是按照上面写出来的程序可能会将/*/看作一个注释语句;解决方法就是在处理注释开始标志时,cin.peek()发现是'*'之后直接用getchar()读掉.
这样我们就可以开始处理引号问题,对于普遍的引号,我们需要设置一个quote_flag来处理,遇到一个就取反!quote_flag(因为英文的前后引号是一样的),这样处理之后我们还要考虑\"的情况,对于这两个字符的处理,我们需要通过ascii码来处理(不然太麻烦),可以在处理台输一个man ascii指令来查,这样这个问题最终结束了.
最终代码:
#include<iostream>
using namespace std;
int main(){
char ch, next_c, last_c='\0';
bool anno_tag=false;//注释标志
bool quote_flag=false;//引号标志
while((ch=getchar())!=EOF){
if(ch==92){ //'\'的asc码
next_c=cin.peek();
if(next_c==34){ //'"'的asc码
getchar();
printf("%c%c",ch,next_c);
continue;
}
}
if(ch=='"'&&!anno_tag) quote_flag=!quote_flag;
if(ch=='/'&&!quote_flag){
if(anno_tag){
if(last_c=='*'){
anno_tag=false;
last_c=ch;
continue;
}
}
else{
next_c=cin.peek();
if(next_c=='*'){
getchar();
anno_tag=true;
}
}
}
if(!anno_tag) printf("%c",ch);
last_c=ch;
}
}
补充小技巧:
对于此题这种输入输出文件太大的情况,利用I/O重定位来处理是一个不错的选择,对于C语言我们有标准I/O的方法来实现重定位.(如果不理解重定位也无所谓)
在main函数中,我们先写这样的两行:
FILE *fd1=freopen("Input.txt","ra",stdin);
FILE *fd2=freopen("Output.txt","wa",stdout);
Input.txt和Output.txt可以是自己取的名字,在工作文件夹里面创建这两个txt文件,"ra"和"wa"是权限位("r"代表read,"w"代表write,"a"代表append).
这样重定位后,我们将要输入的内容放进Input.txt里面,输出的内容就会放到Output.txt里面,我们就不用在狭小的终端里面看输出了.
不要忘了在main函数最后关闭文件:
fclose(fd1);
fclose(fd2);