输入一段代码,去除其中的注释和空行
支持去除以下形式的注释:
//这是一条注释
/*这是一条注释*/
/*这是一条注释*//*这是一条注释*/
/*这是
一条
注释*/
伪代码
读取一行(str,readpos,startpos,endpos,status)
如果状态是1 正常读取如果是2 跨行匹配
那么说明正在读取多行注释的中间部分:跨行匹配
如果该行没有*/
则直接丢弃
如果有*/
将/前面的丢弃
恢复状态1:正常读取
从该行的readpos位置开始读
初始readpos为0如果这行出现//
则丢弃//后面的内容如果这行有/*
标记/位置为起始截取位置
标记为同行待匹配状态2:单行匹配
继续读,如果读到*/
标记/为终止位置
截取,不换行输出
恢复为正常状态:一直读到末尾才换行,去除所有注释
如果没读完,标记readpos,重新调用函数再读一次
如果读完了,readpos=len,之前读到了/*,但仍然没有读取到*/
那么标记为状态2 跨行匹配,读取下一行(注:只是草稿)
流程图
代码
#include<iostream>
#include<fstream>
#include<cstring>
#include<cmath>
using namespace std;
int Check(string readline)
{
//非空串判断
for(int n=0;n<readline.length();n++)
{
//如果有元素不是空格或者回车,该串非空
if(readline[n]!=9&&readline[n]!=32&&readline[n]!=13)
{
//cout<<readline+"非空"<<endl;
return 1;
}
}
return 0;
}
int LineProcess(string readline,int &readpos,int &startpos,int &endpos,int& status)
{
//写入文件
fstream out;
out.open("temp.txt",fstream::out | ios_base::app);
//status 1正常读取 2同行待匹配 3异行待匹配
int j=readline.length();
int flag=0;//没有注释
//cout<<"当前状态:"<<status<<" 当前行:"<<readline<<endl;
//cout<<"当前行:"<<readline<<"当前读取位置:"<<readpos<<readline[readpos]<<endl;
//cout<<" 当前行:"<<readline<<endl;
//跳过空行
if(0==j)
{
//cout<<"空行"<<endl;
return 0;
}
//逐字符读取
for(int n=readpos;n<j;n++)
{
//cout<<"当前状态:"<<status<<"当前读取位置:"<<n<<endl;
//cout<<"进入循环"<<endl;
//cout<<"当前行:"<<readline<<"当前读取位置:"<<readline[n]<<endl;
//读取到//
if(readline[n]=='/'&&readline[n+1]=='/')
{
//cout<<"当前行:"<<readline<<"当前读取位置:"<<readline[n]<<endl;
startpos=n;
//输出/之前的内容
//cout<<"//输出:"<<endl;
//cout<<"//前注释长度:"<<readline.substr(0,n).length()<<endl;
//输出非空字符串
if(Check(readline.substr(0,n)))
{
out<<readline.substr(0,n)<<endl;
cout<<readline.substr(0,n)<<endl;
}
//读取下一行
readpos=0;
return 0;
}
//读取到/*
if(readline[n]=='/'&&readline[n+1]=='*')
{
//cout<<"当前行:"<<readline<<"当前读取位置:"<<readline[n]<<endl;
//标记
//startpos=n;
//输出/之前的内容,不换行,一次只输出一部分
//cout<<"/*输出:"<<endl;
out<<readline.substr(startpos,n-startpos);//0,n
cout<<readline.substr(startpos,n-startpos);//n-startpos
//暂不换行
//状态变更为同行待匹配
status=2;
//标记读取位置为*的下一位
readpos=n+2;
//继续读本行
return 1;
//old
//cout<<"发现注释/*:"<<readline<<endl;
//cout<<startpos<<readline[startpos]<<endpos<<readline[endpos]<<endl;
//system("pause");
//break;
}
//读取到*/
if(readline[n]=='*'&&readline[n+1]=='/')
{
//cout<<"readpos:"<<readpos<<endl;
//恢复正常状态
status=1;
//标记readpos为/的后一位
if(n<j-2)
{
//如果还有,从此开始输出
startpos=n+2;
readpos=n+2;
//cout<<"readpos:"<<readpos<<endl;
//继续读本行
return 1;
}
else
{
//读取下一行
return 0;
}
//if(n!=j-2)return 1;
//else if(j-2==n)return 0;//读取下一行
//old
//flag=3;
//endpos=n+1;
//cout<<"发现注释*/:"<<readline<<endl;
//cout<<startpos<<readline[startpos]<<endpos<<readline[endpos]<<endl;
//system("pause");
//想继续读第二个注释,就不能break
//break;
}
//读取到本行末尾
if(n==j-1)//▲出错点 n==j进入死循环
{
//cout<<"读取到本行末尾"<<endl;
//cout<<"当前状态:"<<status<<endl;
//如果同行待匹配没匹配到,就变成异行待匹配
if(2==status)
{
//cout<<"同行待匹配没匹配到,变成异行待匹配 "<<endl;
status=3;
//读取下一行
readpos=0;
return 0;
}
else if(3==status) //▲出错点 status=3 修改了变量的值
{
//cout<<""<<endl;
//cout<<"是异行待匹配状态,现在读取下一行"<<endl;
//读取下一行
readpos=0;
return 0;
}
else if(1==status)
{
//正常状态
//输出本行未输出过的字符,换行
//cout<<"是正常状态,输出本行,换行,再读取下一行"<<endl;
out<<readline.substr(startpos)<<endl;
cout<<readline.substr(startpos)<<endl;
//读取下一行
readpos=0;
return 0;
}
}
}
//system("pause");
}
void Readin()
{
//读入 :过滤单行的注释,将注释内容全部截取
fstream in;
in.open("InFile.txt",ios::in);
//写入文件
fstream out;
out.open("temp.txt",fstream::out | ios_base::trunc);
int n=0;//当前读取位置
int endpos=0; //用后记得初始化
int startpos=0;
int status=1;
int readpos=0;
string readline;
while (getline(in, readline))
{
//cout<<"读取下一行"<<endl;
int res=0;
startpos=0;
//读取下一行0,继续读本行1
while(res=LineProcess(readline,readpos,startpos,endpos,status))
{
//cout<<"继续读本行"<<endl;
res=0;
//system("pause");
}
}
in.close();
out.close();
return;
}
int main()
{
Readin();
return 0;
}
总结:
先写伪代码,再画流程图,最后写程序,这样可以使得代码逻辑结构清晰,减少出错,节省大量时间。
如果不写伪代码,不画流程图,上来直接就敲,写出来的可能是一座屎山。
虽然能跑,但是逻辑混乱,可读性低,维护困难。
输入
本代码
输出
#include<iostream> #include<fstream> #include<cstring> #include<cmath> using namespace std; int Check(string readline) { for(int n=0;n<readline.length();n++) { if(readline[n]!=9&&readline[n]!=32&&readline[n]!=13) { return 1; } } return 0; } int LineProcess(string readline,int &readpos,int &startpos,int &endpos,int& status) { fstream out; out.open("temp.txt",fstream::out | ios_base::app); int j=readline.length(); int flag=0; if(0==j) { return 0; } for(int n=readpos;n<j;n++) { if(readline[n]=='/'&&readline[n+1]=='/') { startpos=n; if(Check(readline.substr(0,n))) { out<<readline.substr(0,n)<<endl; cout<<readline.substr(0,n)<<endl; } readpos=0; return 0; } if(readline[n]=='/'&&readline[n+1]=='*') { out<<readline.substr(startpos,n-startpos); cout<<readline.substr(startpos,n-startpos); status=2; readpos=n+2; return 1; } if(readline[n]=='*'&&readline[n+1]=='/') { status=1; if(n<j-2) { startpos=n+2; readpos=n+2; return 1; } else { return 0; } } if(n==j-1) { if(2==status) { status=3; readpos=0; return 0; } else if(3==status) { readpos=0; return 0; } else if(1==status) { out<<readline.substr(startpos)<<endl; cout<<readline.substr(startpos)<<endl; readpos=0; return 0; } } } } void Readin() { fstream in; in.open("InFile.txt",ios::in); fstream out; out.open("temp.txt",fstream::out | ios_base::trunc); int n=0; int endpos=0; int startpos=0; int status=1; int readpos=0; string readline; while (getline(in, readline)) { int res=0; startpos=0; while(res=LineProcess(readline,readpos,startpos,endpos,status)) { res=0; } } in.close(); out.close(); return; } int main() { Readin(); return 0; }
最初,没重构之前的代码结构
原先用了一堆flag值,先读取完一行,再跳出来处理
已经读完了一行,就不好继续处理了,这样就很局限了,有很多操作无法实现,比如前面已经读取到了一个注释,后面还有一个,这就没办法了
没用流程图把逻辑关系理顺,这样的逻辑关系过于混乱,自己也不清楚运行到哪一步了,只能一步步慢慢调试修改,甚至有时候修改操作之间会互斥,令人崩溃
此外,这样的代码的复用率低,就比如读取一行,其实可以写一个函数来复用,没有必要在一个函数中写很多重复操作,因为这将大大提高出错的概率
void Readin()
{
while (getline(in, readline))
{
for(n=0;n<j;n++)
{
//过滤单行注释
if(readline[n]=='/'&&readline[n+1]=='/')
{
}
//过滤多行注释
if(readline[n]=='/'&&readline[n+1]=='*')
{
}
if(readline[n]=='*'&&readline[n+1]=='/')
{
}
}
if(flag==1)//有单行注释,截取
{
}
else if(flag==0)//没注释,直接输出
{
}
else if(flag==2)//有多行注释,这是第一行
{
}
else if(flag==4)//有多行注释,该行全部为注释
{
}
else if(flag==3)//注释为单行中的/**/
{
}
}
in.close();
out.close();
return;
}