问题:读取一个cpp文件,去除其中所有的单行注释(//)和多行注释(/**/),将去除注释后的内容写入一个新文件。
注意:
- 不能去除字符串中的任何字符,如 "asdf//gh/**/k" 需要原样输出
- 能够识别转义的双引号(\")和非转义的双引号("),如:
- '\"'(单引号中的转义双引号字符)
- "asdf\"" (字符串中的转义双引号不能表示字符串结束)
- \"aaa"bcdef" (同理,转义双引号不能表示字符串开始)
命令行参数为:
- 参数1:原文件名
- 参数2:新文件名
目录
一、文件流
引入<fstream>头文件,新建ifstream和ofstream对象,如果不能打开文件,则结束整个函数。
#include <iostream>
#include <fstream>
using namespace std;
bool stripComment(string infile,string outfile){
ifstream ifs;
ifs.open(infile,ios::in);
if(!ifs.is_open()){
cout<<"fail to read!"<<endl;
return true;
}
ofstream ofs;
ofs.open(outfile,ios::out|ios::trunc);//写文件,如果已经存在同名的文件,则删除它创建新文件
if(!ofs.is_open()){
cout<<"fail to write!"<<endl;
return true;
}
ifs.close();
ofs.close();
}
int main(int argc,char** argv){
return 0;
}
二、具体逻辑
1.如何循环读入字符
这是最关键的一步。我们利用ifstream对象的get()方法无条件地读取文件流中的一个字符。什么叫无条件?这是与>>运算符相区分的,>>会自动跳过所有空白字符,也就是说,它读不到空格、\t、\n,这不符合我们的需求。
同时,ifstream对象有一个putback()方法,它就是将一个字符重新放回文件流的末尾,下一次get(),我们还会读到它:这有时候很有用。
get(char c) 会把文件流的末尾一个字符取出,放到char型变量c中。这个函数的返回值可以被转换成一个布尔值,表示流的状态:如果流状态正常,则返回true;如果流状态不正常(bad,fail,读到文件尾符号),返回false。
char temp1{};
while(ifs.get(temp1)){
ofs<<temp1;
}
最简单的情况是,利用get()循环读入字符并存入temp1变量中,再把temp1写入新文件中。最终我们会得到与原文件一样的副本。
2.处理单行和多行注释
这两种东西有同一个特点:都是以 / 开头的。所以我们读取到 / 这个字符时,就要小心一点:它是不是意味着我读到了一个注释?
我们可以在此基础上再读一个字符,存入另一个字符变量temp2中,看看它是什么情况:
- / :一定读到了一个单行注释
- * :一定读到了一个多行注释
- 其他字符:刚刚读到的 / 只是一个普通的正斜杠
对于第三种情况, 我们这样善后:
- 立刻输出temp1(/)
- 把temp2放回文件流中等待下一次读取,因为它可能有用(比如,是个双引号)
- 跳转到一开始的while循环
对于第一种情况:
- 一直向后读字符,直到读到一个换行符\n
- 将这个换行符放回文件流
- 跳转到一开始的while循环
第二种情况最麻烦,我们必须找一个办法确定什么时候多行注释才能结束。C++的语法规定, /* 与最近的 */ 之间是多行注释,那么我们只需找最近的 */ 即可。
- while循环一直向后读字符,直到找到一个 *
- 再读一个字符,放入字符变量temp3中。
- temp3是一个 / ,恭喜!我们已经找到多行注释的完整范围,只要简单地break掉当前的while循环,再跳转到开始的while循环就可以。不用输出任何东西。
- 不好,temp3不是 / ,这意味着我们没有找到多行注释的终止处。此时应该将temp3放回文件流,返回步骤1:这是很必要的,如果temp3正好是一个* ,它之后恰好是一个 / 呢?我们不能丢弃任何“可能有用”的字符!</
文章评论