:::info
题目:文本查询程序
实现一个文本查询程序。
需求
1. 使用 C++ 完成代码,确保程序可由 C++ 编译器(如g++)编译。
2. 采用面向对象的思想编写程序,以使程序更为优雅可靠。程序应该尽可能符合正常使用习
惯。
3. 程序实现两个功能,一个是文件格式化功能,另一个是文本查询功能,打开程序时可以选
择功能,类似如下
选择需要的功能:
1. 文件格式化
2. 文本查询
3. 退出
4. 进入文件格式化功能,程序会根据用户后续输入的文件位置(绝对地址或相对地址)和文
件名读取文件,并由用户指定每行最大字符数 n,处理文件,使得文件在行数到达 n 时进
行换行(若为英文,需要切断单词时,提前换行,避免单词被切断,且需使得标点符号不
在行首),并由用户指定保存新文件的位置与新文件名,若位置与新文件名已存在,由用
户确认是否替换(原有文件内容不可改变,除非新文件与原文件在相同位置且有相同文件
名,并被确认替换)。
5. 文本查询允许用户指定一个文件(给定文件位置与文件名),并在其中查询单词,查询结
果是单词在文件中出现的次数及其所在行的列表。如果一个单词在一行中出现多次,此行
只列出一次。行会按照升序输出,即第7行会在第9行之前显示,以此类推。如果查询不
到,则不输出。
6. 文本查询功能可以通过交互式的方式保存用户输入的字符串(与项目二一样,忽略输入时
的各种空格),并可以支持逻辑查询操作(各种逻辑运算)。
10. 尝试支持批量读取文件,并进行针对多文件的查询。
11. 尝试支持将查询结果输出到新文件中,而非打印,如
>>> a | b >> ./result.txt #将结果输出到当前目录的文件result.txt中
12. 可尝试多种方式对代码进行优化,包括执行速度、功能等;可以尝试完善错误提示,让错
误提示更加明确;可内嵌帮助文档,通过特定指令能够输出对应帮助文档。
13. 可以使用 STL 等库进行实现,但不能直接采用功能非常类似的他人的程序。
14. 项目报告需对需求进行分析,描述实现方案与项目特色。报告应包含核心代码并进行解
释。如果编译运行复杂,请提供编译运行代码的说明。提供运行结果的展示(如截图),
并尽量给出性能展示与分析。最后给出总结。同时,鼓励写上实现代码时遇到的困难以及
相应的解决思路/方案。
15. 允许适当发挥,使得程序功能更加丰富。
16. 提示:可以参考书籍《C++ Primer 第五版》12.3节与15.9节内容。
:::
实验报告解析
写在前面
得到的一些小细节启发
1.智能指针的初始化
1 2 3
| shared_ptr<set<int>> ptr=make_shared<set<int>>()
|
2.ofstrem.wirte()方法适用于写二进制数据,而不是文本。
开始解析
:::info
1. 使用 C++ 完成代码,确保程序可由 C++ 编译器(如g++)编译。
2. 采用面向对象的思想编写程序,以使程序更为优雅可靠。程序应该尽可能符合正常使用习
惯。
:::
不需要装多余的库,我们需要用到的基本上STL都有,直接用STL和算法库就行了。因此不需要加多余的编译选项,能够通过编译就行。
本项目是为了实现一个文本查询的功能,需要读写文件操作,需要有文章,打印行号等,为了符合面向对象的思想,同时参考第16点提示去看了一下C++ Primer第五版的对应内容,设计了如下的类,为了适应本程序,所以类里面的对象和方法有一点小小改动。
接下来我们展示我们用到的类
首先是QueryResult和TextQuery类,第一个类设计的目的是为了保存查询后的结果,第二个TextQuery类设计的目的是为了打开文件进行读取操作,然后对文章的内容进行解析。
下面介绍这两个类的功能与作用,以及设计的思想:
基于面向对象的所用类介绍
对于TextQuery
首先对于一篇文章的获取首先要打开文件,程序要求我们查询单词,返回对应的行号和内容。
那么我们首先就要有一个容器存储行号,最好的是set,因为它无序性,不重复。
随后我们输入单词,要返回行号,明显是键值对的结构,这里采用map<string,set>,让它做自己排序
然后要打印其对应行号的内容,这说明我们要存储文章,采用vector存储文章,每个元素就是它的句子。
对于第一个方法,我们在构造函数的时候,就应该把这两个成员处理好,我们读入文章首先要一个文件流,所以传参进来的是一个读入的文件流ifstream。然后一行一行读取进行分割就是。
第二个方法,我们query方法查找要返回一个结果类,结果QueryResult类的代码放在下面一个小标题,我们需要一个string来查询,根据查询的结果(有则返回对应的值,无则返回空集合)采用QueryResult的构造函数返回。
那么问题来了,为了让QueryResult打印出对应的内容,QueryResult也需要传入一个存储文章的容器,这样直接拷贝的话就很麻烦,以及浪费空间,如果直接用TextQuery的file可能还有作用域非法访问的问题,所以我们把TextQuery的成员全部设置成共享指针,让shared_ptr来管理,因此才如代码所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| class TextQuery{ private: shared_ptr<vector<string>> file; map<string,shared_ptr<set<unsigned>>> wordmap; public:
TextQuery(ifstream& is):file(new vector<string>) { string text; while(getline(is,text)) { file->push_back(text); int n=file->size()-1; istringstream line(text); string word; while(line >> word) { shared_ptr<set<unsigned>> lines; if(!wordmap.count(word)){ lines = make_shared<set<unsigned>>(); wordmap[word] = lines; } wordmap[word]->insert(n); } } } QueryResult query(const string& inputword)const { static shared_ptr<set<unsigned>> nodata(new set<unsigned>()); auto loc=wordmap.find(inputword); if(loc == wordmap.end()){ return QueryResult(inputword,nodata,file); }else{ return QueryResult(inputword,loc->second,file); } }
};
|
对于QueryResult
对于返回的结果类,我们首先肯定保存有指向文章vector的共享指针,然后还要保存选出来的行号set的共享指针,同时保存需要查询的单词。
同理,该让共享指针管理的就让它管理。该类具有一些获取本成员的方法,以及打印的方法。
在以后得利用差集计算非集,所以也弄了个返回指向文章全部行号的集合的共享指针的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| class QueryResult{ private: string inputword; shared_ptr<set<unsigned>> idx; shared_ptr<vector<string>> file; public: QueryResult(string s,shared_ptr<set<unsigned>> i,shared_ptr<vector<string>> f):inputword(s),idx(i),file(f){
} void print() { cout<<"\""<<this->inputword<<"\"出现了"<<this->idx->size()<<"次"<<endl; for(auto num:*(this->idx)){ cout<<" (line "<<num+1<<") "; cout<<*(this->file->begin()+num)<<endl; } } shared_ptr<vector<string>> getfile(){ return file; } shared_ptr<set<unsigned>> getset(){ return idx; } shared_ptr<set<unsigned>> getfileset(){ shared_ptr<set<unsigned>> fileset =make_shared<set<unsigned>>();; for(int i=0;i<file->size();i++){ fileset->insert(i); } return fileset; } };
|
到这里,已经可以实现查询一个单词的功能了
关于采用逻辑查询操作的类
我们需要重载逻辑运算,逻辑运算单目是~,双目是|和&,这就是它们的区别。
运算过程中,运算的操作数是什么类型的,运算后的结果又是什么类型?
虽然我们可以运算出一个集合,然后让两个集合进行逻辑运算,这样子可以实现eval计算的基本思路,但是如果我们还需要别方法呢,比如知道当前是在查询什么东西(如知道查询”~(a|b)”)
我们不妨设计一个类,参考书本设计了对应的类
我们按照对应的运算设计对应的类,如NotQuery、OrQuery、AndQuery,其中双目运算的类继承于BinaryQuery,然后再继承于QueryBase,NotQuery直接继承于QueryBase。然后就是各自对QueryBase的虚函数以及运算符进行重载
我们让运算返回一个结果,这个结果如果是让共享指针去管理,那么就可以省很多问题,因此设计了Query,有一个成员是指向QueryBase类型的一个共享指针。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
| class QueryBase{ friend class Query; protected: unsigned idx; virtual ~QueryBase(){}; private: virtual QueryResult eval(const TextQuery&)const{} virtual string rep()const{} };
class WordQuery : public QueryBase{ friend class Query; public: ~WordQuery() override{ } private: WordQuery(const string &s):query_word(s){} QueryResult eval(const TextQuery &t)const{ return t.query(query_word); } string rep() const{ return query_word; } string query_word; };
class Query{ friend Query operator ~(const Query&); friend Query operator |(const Query& ,const Query &); friend Query operator &(const Query& ,const Query &); private: shared_ptr<QueryBase> q; public: Query(shared_ptr<QueryBase> query):q(query){}; Query& operator =(const Query& que){ q=que.q; return *this; } Query(const string &s){ shared_ptr<WordQuery> ptr(new WordQuery(s)); q=ptr; } QueryResult eval(const TextQuery &t)const{ return q->eval(t); } string rep ()const{ return q->rep(); } friend ostream & operator<<(ostream &os,const Query& query) { return os << query.rep(); } };
class NotQuery : public QueryBase{ friend Query operator ~(const Query &); public: ~NotQuery() override{ } private: NotQuery(const Query &q):query(q){} string rep()const{ return "~("+query.rep()+")"; } QueryResult eval(const TextQuery & text)const { auto sml=query.eval(text); auto smlset=sml.getset(); auto allset=sml.getfileset(); set<unsigned> resultset; set_difference(allset->begin(),allset->end(),smlset->begin(),smlset->end(),inserter(resultset,resultset.begin())); shared_ptr<set<unsigned>> resultptr=make_shared<set<unsigned>>(resultset); return QueryResult(rep(),resultptr,sml.getfile()); } Query query; }; inline Query operator~ (const Query &operand){ return shared_ptr<QueryBase>(new NotQuery(operand)); } class BinaryQuery : public QueryBase{ friend Query operator ~(const Query &); protected: BinaryQuery(const Query &l,const Query &r,string s):lhs(l),rhs(r),opSym(s){} string rep()const{ return "~("+lhs.rep()+" "+opSym+" "+rhs.rep()+")"; } Query lhs,rhs; string opSym; };
class OrQuery : public BinaryQuery{ friend Query operator |(const Query &,const Query &); public: ~OrQuery() override{ } private: OrQuery(const Query &l,const Query & r):BinaryQuery(l,r,"|"){} QueryResult eval(const TextQuery &text)const { auto r=rhs.eval(text),l=lhs.eval(text); auto retlines=l.getset(); retlines->insert(r.getset()->begin(),r.getset()->end()); return QueryResult(rep(),retlines,l.getfile()); }
}; inline Query operator| (const Query &lhs,const Query &rhs){ return shared_ptr<QueryBase>(new OrQuery(lhs,rhs)); } class AndQuery : public BinaryQuery{ friend Query operator &(const Query &,const Query &); public: ~AndQuery() override{ } private: AndQuery(const Query &l,const Query & r):BinaryQuery(l,r,"&"){} QueryResult eval(const TextQuery &text)const { auto r=rhs.eval(text),l=lhs.eval(text); auto lset=l.getset(),rset=r.getset(); set<unsigned> resultset; set_intersection(lset->begin(),lset->end(),rset->begin(),rset->end(),inserter(resultset,resultset.begin())); shared_ptr<set<unsigned>> resultptr=make_shared<set<unsigned>>(resultset); return QueryResult(rep(),resultptr,l.getfile()); } }; inline Query operator& (const Query &lhs,const Query &rhs){ return shared_ptr<QueryBase>(new AndQuery(lhs,rhs)); }
|
关于文件资源管理的类(RAII的思想体现)
对于ofstream和ifstream,设计了对应的类管理对象,这样可以让其在不需要的时候调用析构函数。
在构造函数设计了一些东西,比如如果文件流已打开怎么处理,以及文件是否需要覆写。
以及获取当前管理文件流引用的方法,方便其他类方法的传参。
对于FileFormatter是为了实现需求4而设计,含有IFileStream和OfileStream的对象。
首先读取ifstream里的文章是以单词形式读取,这是为了方便格式化处理,让单词一个一个拼接。拼接好了达到要求就放到一个string,然后放到本类的vector newbuf存储,之后再将句子写到ofstream流里即可。
如果下一个句子有句号开头的情况,就直接拼接到本次string后面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
| class OFileStream{ private: string FilePath; ofstream ofile; public: OFileStream(const string &name){ ofile.close(); FilePath=name; if(FileExists(name)){ cout<<"文件存在,是否进行覆写?(Y/N):"; char response; cin>>response; cin.ignore(); if(response=='Y'||response=='y'){ ofile.close(); ofile.open(name,ios::out); if(ofile.is_open()){ cout<<"文件"<<name<<"成功打开,请写入 :)"<<endl; }else{ cerr<<"文件"<<name<<"打开出现异常 :("<<endl; } }else{ cout<<"保留原有文件,不进行操作"<<endl; } }else{ ofile.close(); ofile.open(name,ios::out); if(ofile.is_open()){ cout<<"文件"<<name<<"成功打开,请写入 :)"<<endl; }else{ char ch='0'; throw ch; } } } ~OFileStream(){ FilePath=""; if(ofile.is_open()){ ofile.close(); } } ofstream& getofsteram(){ return ofile; } };
class IFileStream{ private: string FilePath; ifstream ifile; public: IFileStream(const string &name){ FilePath=name; if(ifile.is_open()){ cout<<"文件"<<name<<"已被打开过... :|"<<endl; } else{ ifile.open(name); if(ifile.is_open()){ cout<<"文件"<<name<<"可以读取 :)"<<endl; }else{ string something="文件"+name+"读取出现异常,不存在的路径 :("; throw something; } } } ~IFileStream(){ FilePath=""; if(ifile.is_open()){ ifile.close(); } }
ifstream& getifsteram(){ return ifile; } }; class FileFormatter{ private: OFileStream ost; IFileStream ist; vector<string> vecbuf; vector<string> newbuf; unsigned num; public: FileFormatter(const string FilePath,const string newFilePath,unsigned n):ist(FilePath),ost(newFilePath),num(n),vecbuf(),newbuf(){ } void readFromFile() { stringstream ss; string temp,t; char c[num+10]; while (getline(ist.getifsteram(),temp)){ ss<<temp; while(ss >> t){ vecbuf.push_back(t); } ss.clear(); } } void formatText() { while(vecbuf.size()){ string str=""; if(vecbuf.front().length()>=num){ newbuf.push_back(vecbuf.front()); vecbuf.erase(vecbuf.begin()); }else{ while(str.length()<num){ bool flag=vecbuf.front()=="."||vecbuf.front()==";"||vecbuf.front()=="!"||vecbuf.front()=="\""||vecbuf.front()==","||vecbuf.front()==":"||vecbuf.front()=="?"; if(str.length()==0){ if(flag){ cerr<<"句首出现标点符号"<<endl; } if(!vecbuf.empty()){ str=vecbuf.front(); vecbuf.erase(vecbuf.begin()); } } else if((str.length()+vecbuf.front().length()+1)<=num&&!flag){ if(!vecbuf.empty()){ str=str+" "+vecbuf.front(); vecbuf.erase(vecbuf.begin()); } } else if(flag){ while(flag){ if(!vecbuf.empty()){ str=str+" "+vecbuf.front(); vecbuf.erase(vecbuf.begin()); flag=vecbuf.front()=="."||vecbuf.front()==";"||vecbuf.front()=="!"||vecbuf.front()=="\""||vecbuf.front()==","||vecbuf.front()==":"||vecbuf.front()=="?"; }else{ flag=false; } } flag=false; } else{ break; } if(vecbuf.empty()){ break; } } newbuf.push_back(str); } }
} void saveToFile() { ofstream& outfile=ost.getofsteram(); for(const string &s:newbuf){ outfile<<s<<endl; } } };
|
解析需求3,4,5
:::info
- 程序实现两个功能,一个是文件格式化功能,另一个是文本查询功能,打开程序时可以选择功能,类似如下
选择需要的功能:
1. 文件格式化
2. 文本查询
3. 退出
:::
main函数就是针对需求3来设计,然后该读取的读取,该调用对的调用,以及可能涉及到的一些异常处理。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| int main() { int cho; string buf;
string newbuf; while(1){ menu(); cin>>cho; cin.ignore(); switch (cho) { case 1: cout<<"请输入读取的文件路径"<<endl; cout<<">>> "; getline(cin,buf); cout<<"请指定每行最大的字符数n :)"<<endl; cout<<">>>"; int num; cin>>num; cin.ignore(); cout<<"请指定新文件保存的路径 :)"<<endl; cout<<">>>"; getline(cin,newbuf); try{ ff=make_shared<FileFormatter>(buf,newbuf,num); ff->readFromFile(); ff->formatText(); ff->saveToFile(); }catch(string str){ cerr<<"错误的文件路径"<<buf<<"导致打开异常 :("<<endl; }catch(char ch){ cerr<<"错误的文件路径"<<buf<<"导致写入异常 :("<<endl; } break; case 2: cout<<"请输入读取的文件路径"<<endl; cout<<">>> "; getline(cin,newbuf); try{ ifs=make_shared<IFileStream>(newbuf); cout<<">>> "; getline(cin,buf); BlankFliter(buf); StringTraslate(buf); }catch(string str){ cerr<<str; } break; case 3: exit(0); break; default: cout<<"无效的选项:("<<endl; break; } } return 0; }
|
:::info
4. 进入文件格式化功能,程序会根据用户后续输入的文件位置(绝对地址或相对地址)和文
件名读取文件,并由用户指定每行最大字符数 n,处理文件,使得文件在行数到达 n 时进
行换行(若为英文,需要切断单词时,提前换行,避免单词被切断,且需使得标点符号不
在行首),并由用户指定保存新文件的位置与新文件名,若位置与新文件名已存在,由用
户确认是否替换(原有文件内容不可改变,除非新文件与原文件在相同位置且有相同文件
名,并被确认替换)。
:::
调用在main的代码已经写了,对应FileFormatter类的设计已在需求2展示,这里直接展示结果


:::info
5. 文本查询允许用户指定一个文件(给定文件位置与文件名),并在其中查询单词,查询结
果是单词在文件中出现的次数及其所在行的列表。如果一个单词在一行中出现多次,此行
只列出一次。行会按照升序输出,即第7行会在第9行之前显示,以此类推。如果查询不
到,则不输出。例如,在读取一个文件后,在其中寻找单词element时,输出结果的前几行
应该类似这样:
>>> element
“element” 出现 112 次
(line 36) An element contains only a key;
(line 158) operator creates a new element
(line 160) Regardless of whether the element
(line 168) When we fetch an element from a map, we
(line 214) If the element is not found, find returns
:::

结果如图,相对应的函数,定义了一个StringTraslate和ExpTraslate函数实现对应的效果,以及一些辅助函数如空格过滤,判断输入的字符串是变量还是常量等。
StringTraslate主要是按照操作符分割字符串,已经判断是查询操作还是赋值操作
ExpTraslate先是把StringTraslate处理后的字符串设法转换成后缀表达式,然后利用栈完成运算(利用栈也是为了支持复杂运算)。
对应的栈结构声明为stack<shared_ptr>,这样就不会因为生存周期的问题造成非法内存访问。
最后ExpTraslate调用 qstack.top()->eval(TextQuery(ifs->getifsteram())).print();相关的语句,就可以完成结果效果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
| shared_ptr<IFileStream> ifs; shared_ptr<FileFormatter> ff; void BlankFliter(string &str) { str.erase(std::remove_if(str.begin(), str.end(), ::isspace), str.end()); } void ExpTraslate(string &buf); string IsNameReturn(string &buf){ string result; auto iter = umap.find(buf); if(iter!=umap.end()){ result=iter->second; return result; } return buf; } void StringTraslate(string &buf) { if(buf[0]=='='){ buf.erase(buf.begin()); }
int temp=0; int times=0; string name,value;
for(int i=0;i<buf.size()-1;i++){ if(buf[i]=='>'&&buf[i+1]=='>'){ filename=buf.substr(i+2,buf.size()-i-2); buf=buf.substr(0,i); filesearch=1; break; } } for(int i=1;i<buf.length();i++){
if(buf[i]=='='){ for(int j=i;j<buf.length();j++){ if(buf[j]=='\"'&×==0){ temp=j+1; times++; }else if(buf[j]=='\"'&×==1){ value=buf.substr(temp,j-temp); times++; break; } if(j==buf.length()-1){ string qwq=buf.substr(temp,j-temp); auto iter = umap.find(qwq); if(iter!=umap.end()){ value=iter->second; break; } else{ cerr<<"输入了一个不存在的变量,如果需要输入常量abcd,请用\"abcd\"包裹常量abcd :("<<endl; } } } if(times%2){ cerr<<"请确保\"abcd\"包裹常量abcd :("<<endl; } name=buf.substr(0,i); umap[name]=value; return ; } } ExpTraslate(buf); } bool isOperater(string str){ if(str=="|"||str=="&"||str=="~"){ return true; } return false; } int Precedence(char op) { if (op == '~') return 3; if (op == '&') return 2; if (op == '|') return 1; else return 0; } void ExpTraslate(string &buf){ stack<shared_ptr<Query>> qstack; stack<char> opstack; char op; vector<string> backexp; int word_start=0; bool last_right=buf[buf.size()-1]==')'; bool Had_op=0; int priority1; for(int i=0;i<buf.size();i++){ if(buf[i]=='~'||buf[i]=='|'||buf[i]=='&'||buf[i]=='('||buf[i]==')'){ Had_op=1; op=buf[i]; priority1=Precedence(op); if (opstack.empty()||op=='('){ opstack.push(op); if(i-word_start>0){ backexp.push_back(buf.substr(word_start,i-word_start)); } word_start=i+1; } else if(op ==')'){ if(i-word_start>0){ backexp.push_back(buf.substr(word_start,i-word_start)); } while(!opstack.empty()&&opstack.top()!='('){ string tempop(1,opstack.top()); opstack.pop(); backexp.push_back(tempop); } if(!opstack.empty()&&opstack.top()=='('){ opstack.pop(); } else{ cerr<<"表达式括号不匹配,请检查并重启程序 :("<<endl; exit(-1); } word_start=i+1; } else if(op=='~'||op=='|'||op=='&') { if(i-word_start>0){ backexp.push_back(buf.substr(word_start,i-word_start)); } while((!opstack.empty())&&(Precedence(opstack.top())>=priority1)){ string tempop(1,opstack.top()); opstack.pop(); backexp.push_back(tempop); } opstack.push(op); word_start=i+1; } } } if(!last_right){ backexp.push_back(buf.substr(word_start,buf.size()-word_start)); } while(!opstack.empty()){ string tempop(1,opstack.top()); opstack.pop(); backexp.push_back(tempop); } if(!Had_op){ Query onetime(IsNameReturn(buf)); if(filesearch){ OFileStream ofi(filename); cout<<onetime.rep()<<endl; onetime.eval(TextQuery(ifs->getifsteram())).write(ofi.getofsteram()); }else{ cout<<onetime.rep()<<endl; onetime.eval(TextQuery(ifs->getifsteram())).print(); return; } } while(!backexp.empty()){ if(isOperater(backexp.front())) { string opstr=backexp.front(); if(opstr=="|"){ if(qstack.size()<2){ cerr<<"表达式所需要的操作数不够... :("<<endl; return ; } shared_ptr<Query> temp1=qstack.top(); qstack.pop(); shared_ptr<Query> temp2=qstack.top(); qstack.pop(); shared_ptr<Query> result=make_shared<Query>(*temp1.get()|*temp2.get()); qstack.push(result); }else if(opstr=="&"){ if(qstack.size()<2){ cerr<<"表达式所需要的操作数不够... :("<<endl; return ; } shared_ptr<Query> temp1=qstack.top(); qstack.pop(); shared_ptr<Query> temp2=qstack.top(); qstack.pop(); shared_ptr<Query> result=make_shared<Query>(*temp1.get()&*temp2.get()); qstack.push(result); }else if(opstr=="~"){ if(qstack.size()<1){ cerr<<"表达式所需要的操作数不够... :("<<endl ; return ; } shared_ptr<Query> temp=qstack.top(); qstack.pop(); shared_ptr<Query> result=make_shared<Query>(~(*temp.get())); qstack.push(result); } backexp.erase(backexp.begin()); } else{ qstack.push(make_shared<Query>(IsNameReturn(backexp.front()))); backexp.erase(backexp.begin()); } } if(qstack.size()==1){ if(filesearch){ OFileStream ofi(filename); qstack.top()->eval(TextQuery(ifs->getifsteram())).write(ofi.getofsteram()); }else{ qstack.top()->eval(TextQuery(ifs->getifsteram())).print(); } }else{ cerr<<"表达式可能出现错误,无法运算出唯一结果"<<endl; exit(-1); } }
|
解析需求6
:::info
文本查询功能可以通过交互式的方式保存用户输入的字符串(与项目二一样,忽略输入时
的各种空格),并可以支持逻辑查询操作(各种逻辑运算)。


:::
还是老样子涉及到unordered_map
1 2 3 4 5 6 7 8 9 10 11 12
| unordered_map<string,string>umap{
}; string IsNameReturn(string &buf){ string result; auto iter = umap.find(buf); if(iter!=umap.end()){ result=iter->second; return result; } return buf; }
|
解析需求7
:::info
7. 要能够打印各种错误信息,比如用户输入不符合规范时。
:::
OFileStream和IFileStream里面都有对应的错误异常处理
输入的路径有问题,就返回主函数重新执行菜单

输入的查询表达式有问题,返回主函数


解析需求9
:::info
9. 尝试支持带括号控制优先级的复杂运算(优先级参照C++运算符优先级),如
>>> ((~a | b & c) | d) & d
:::
利用栈就可以实现复杂表达式的运算,就是优先级的问题。代码在需求5已经贴出

解析需求11
可以看到能够成功写到文件中,其实就是字符串处理操作中,根据>>分割成前后部分,后面打开文件即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class QueryResult{ private: string inputword; shared_ptr<set<unsigned>> idx; shared_ptr<vector<string>> file; public: void write(ofstream& ofs){ ofs<<"\""<<this->inputword<<"\"出现了"<<this->idx->size()<<"次"<<endl; for(auto num:*(this->idx)){ ofs<<" (line "<<num+1<<") "; ofs<<*(this->file->begin()+num)<<endl; } .......... ........... ......... }
|

总结
算是比较正常的完成了一个程序,有了正确的思路和设计方向就是省了很多纠错的时间,对类和对象的印象更加深刻了,以及多态继承等概念,还有共享指针的妙处就是跨越{}作用域给你传输数据。解决了上次没有解决复杂表达式的问题,果然还是直接设计面向对象的栈类型是最佳的,以及重载需要的操作符,让我们更加方便的对对象进行操作。复习了RAII和异常处理类操作,没有用上模板但是没关系,也不强求,看需求。
不足之处感觉就是没有完成所有任务,感觉还是自己不够下功夫。
看了C++prinmer的相关章节,认识到自己还是需要看一遍这本书的,对于内存操作感觉还是有点陌生,以及共享指针的用处,看了才知道怎么用。
源代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709
| #include <iostream> #include <sstream> #include <fstream> #include <memory> #include <string> #include <vector> #include <map> #include <unordered_map> #include <stack> #include <set> #include <algorithm>
using namespace std; #define MAX 1024 unordered_map<string,string>umap{
}; string IsNameReturn(string &buf); string filename; bool filesearch=0; bool FileExists(const string& filename) { ifstream file(filename); return file.good(); }
class QueryResult{ private: string inputword; shared_ptr<set<unsigned>> idx; shared_ptr<vector<string>> file; public: QueryResult(string s,shared_ptr<set<unsigned>> i,shared_ptr<vector<string>> f):inputword(s),idx(i),file(f){
} void print() { cout<<"\""<<this->inputword<<"\"出现了"<<this->idx->size()<<"次"<<endl; for(auto num:*(this->idx)){ cout<<" (line "<<num+1<<") "; cout<<*(this->file->begin()+num)<<endl; } } void write(ofstream& ofs){ ofs<<"\""<<this->inputword<<"\"出现了"<<this->idx->size()<<"次"<<endl; for(auto num:*(this->idx)){ ofs<<" (line "<<num+1<<") "; ofs<<*(this->file->begin()+num)<<endl; } } shared_ptr<vector<string>> getfile(){ return file; } shared_ptr<set<unsigned>> getset(){ return idx; } shared_ptr<set<unsigned>> getfileset(){ shared_ptr<set<unsigned>> fileset =make_shared<set<unsigned>>();; for(int i=0;i<file->size();i++){ fileset->insert(i); } return fileset; }
}; class TextQuery{ private: shared_ptr<vector<string>> file; map<string,shared_ptr<set<unsigned>>> wordmap; public: TextQuery(ifstream& is):file(new vector<string>) { string text; while(getline(is,text)) { file->push_back(text); int n=file->size()-1; istringstream line(text); string word; while(line >> word) { shared_ptr<set<unsigned>> lines; if(!wordmap.count(word)){ lines = make_shared<set<unsigned>>(); wordmap[word] = lines; } wordmap[word]->insert(n); } } } QueryResult query(const string& inputword)const { static shared_ptr<set<unsigned>> nodata(new set<unsigned>()); auto loc=wordmap.find(inputword); if(loc == wordmap.end()){ return QueryResult(inputword,nodata,file); }else{ return QueryResult(inputword,loc->second,file); } }
};
class QueryBase{ friend class Query; protected: unsigned idx; virtual ~QueryBase(){}; private: virtual QueryResult eval(const TextQuery&)const{} virtual string rep()const{} };
class WordQuery : public QueryBase{ friend class Query; public: ~WordQuery() override{ } private: WordQuery(const string &s):query_word(s){} QueryResult eval(const TextQuery &t)const{ return t.query(query_word); } string rep() const{ return query_word; } string query_word; };
class Query{ friend Query operator ~(const Query&); friend Query operator |(const Query& ,const Query &); friend Query operator &(const Query& ,const Query &); private: shared_ptr<QueryBase> q; public: Query(shared_ptr<QueryBase> query):q(query){}; Query& operator =(const Query& que){ q=que.q; return *this; } Query(const string &s){ shared_ptr<WordQuery> ptr(new WordQuery(s)); q=ptr; } QueryResult eval(const TextQuery &t)const{ return q->eval(t); } string rep ()const{ return q->rep(); } friend ostream & operator<<(ostream &os,const Query& query) { return os << query.rep(); } };
class NotQuery : public QueryBase{ friend Query operator ~(const Query &); public: ~NotQuery() override{ } private: NotQuery(const Query &q):query(q){} string rep()const{ return "~("+query.rep()+")"; } QueryResult eval(const TextQuery & text)const { auto sml=query.eval(text); auto smlset=sml.getset(); auto allset=sml.getfileset(); set<unsigned> resultset; set_difference(allset->begin(),allset->end(),smlset->begin(),smlset->end(),inserter(resultset,resultset.begin())); shared_ptr<set<unsigned>> resultptr=make_shared<set<unsigned>>(resultset); return QueryResult(rep(),resultptr,sml.getfile()); } Query query; }; inline Query operator~ (const Query &operand){ return shared_ptr<QueryBase>(new NotQuery(operand)); } class BinaryQuery : public QueryBase{ friend Query operator ~(const Query &); protected: BinaryQuery(const Query &l,const Query &r,string s):lhs(l),rhs(r),opSym(s){} string rep()const{ return "("+lhs.rep()+" "+opSym+" "+rhs.rep()+")"; } Query lhs,rhs; string opSym; };
class OrQuery : public BinaryQuery{ friend Query operator |(const Query &,const Query &); public: ~OrQuery() override{ } private: OrQuery(const Query &l,const Query & r):BinaryQuery(l,r,"|"){} QueryResult eval(const TextQuery &text)const { auto r=rhs.eval(text),l=lhs.eval(text); auto retlines=l.getset(); retlines->insert(r.getset()->begin(),r.getset()->end()); return QueryResult(rep(),retlines,l.getfile()); }
}; inline Query operator| (const Query &lhs,const Query &rhs){ return shared_ptr<QueryBase>(new OrQuery(lhs,rhs)); } class AndQuery : public BinaryQuery{ friend Query operator &(const Query &,const Query &); public: ~AndQuery() override{ } private: AndQuery(const Query &l,const Query & r):BinaryQuery(l,r,"&"){} QueryResult eval(const TextQuery &text)const { auto r=rhs.eval(text),l=lhs.eval(text); auto lset=l.getset(),rset=r.getset(); set<unsigned> resultset; set_intersection(lset->begin(),lset->end(),rset->begin(),rset->end(),inserter(resultset,resultset.begin())); shared_ptr<set<unsigned>> resultptr=make_shared<set<unsigned>>(resultset); return QueryResult(rep(),resultptr,l.getfile()); } }; inline Query operator& (const Query &lhs,const Query &rhs){ return shared_ptr<QueryBase>(new AndQuery(lhs,rhs)); }
class OFileStream{ private: string FilePath; ofstream ofile; public: OFileStream(const string &name){ ofile.close(); FilePath=name; if(FileExists(name)){ cout<<"文件存在,是否进行覆写?(Y/N):"; char response; cin>>response; cin.ignore(); if(response=='Y'||response=='y'){ ofile.close(); ofile.open(name,ios::out); if(ofile.is_open()){ cout<<"文件"<<name<<"成功打开,请写入 :)"<<endl; }else{ cerr<<"文件"<<name<<"打开出现异常 :("<<endl; } }else{ cout<<"保留原有文件,不进行操作"<<endl; } }else{ ofile.close(); ofile.open(name,ios::out); if(ofile.is_open()){ cout<<"文件"<<name<<"成功打开,请写入 :)"<<endl; }else{ char ch='0'; throw ch; } } } ~OFileStream(){ FilePath=""; if(ofile.is_open()){ ofile.close(); } } ofstream& getofsteram(){ return ofile; } };
class IFileStream{ private: string FilePath; ifstream ifile; public: IFileStream(const string &name){ FilePath=name; if(ifile.is_open()){ cout<<"文件"<<name<<"已被打开过... :|"<<endl; } else{ ifile.open(name); if(ifile.is_open()){ cout<<"文件"<<name<<"可以读取 :)"<<endl; }else{ string something="文件"+name+"读取出现异常,不存在的路径 :("; throw something; } } } ~IFileStream(){ FilePath=""; if(ifile.is_open()){ ifile.close(); } }
ifstream& getifsteram(){ return ifile; } }; class FileFormatter{ private: OFileStream ost; IFileStream ist; vector<string> vecbuf; vector<string> newbuf; unsigned num; public: FileFormatter(const string FilePath,const string newFilePath,unsigned n):ist(FilePath),ost(newFilePath),num(n),vecbuf(),newbuf(){ } void readFromFile() { stringstream ss; string temp,t; char c[num+10]; while (getline(ist.getifsteram(),temp)){ ss<<temp; while(ss >> t){ vecbuf.push_back(t); } ss.clear(); } } void formatText() { while(vecbuf.size()){ string str=""; if(vecbuf.front().length()>=num){ newbuf.push_back(vecbuf.front()); vecbuf.erase(vecbuf.begin()); }else{ while(str.length()<num){ bool flag=vecbuf.front()=="."||vecbuf.front()==";"||vecbuf.front()=="!"||vecbuf.front()=="\""||vecbuf.front()==","||vecbuf.front()==":"||vecbuf.front()=="?"; if(str.length()==0){ if(flag){ cerr<<"句首出现标点符号"<<endl; } if(!vecbuf.empty()){ str=vecbuf.front(); vecbuf.erase(vecbuf.begin()); } } else if((str.length()+vecbuf.front().length()+1)<=num&&!flag){ if(!vecbuf.empty()){ str=str+" "+vecbuf.front(); vecbuf.erase(vecbuf.begin()); } } else if(flag){ while(flag){ if(!vecbuf.empty()){ str=str+" "+vecbuf.front(); vecbuf.erase(vecbuf.begin()); flag=vecbuf.front()=="."||vecbuf.front()==";"||vecbuf.front()=="!"||vecbuf.front()=="\""||vecbuf.front()==","||vecbuf.front()==":"||vecbuf.front()=="?"; }else{ flag=false; } } flag=false; } else{ break; } if(vecbuf.empty()){ break; } } newbuf.push_back(str); } }
} void saveToFile() { ofstream& outfile=ost.getofsteram(); for(const string &s:newbuf){ outfile<<s<<endl; } } }; shared_ptr<IFileStream> ifs; shared_ptr<FileFormatter> ff; void BlankFliter(string &str) { str.erase(std::remove_if(str.begin(), str.end(), ::isspace), str.end()); } void ExpTraslate(string &buf); string IsNameReturn(string &buf){ string result; auto iter = umap.find(buf); if(iter!=umap.end()){ result=iter->second; return result; } return buf; } void StringTraslate(string &buf) { if(buf[0]=='='){ buf.erase(buf.begin()); }
int temp=0; int times=0; string name,value;
for(int i=0;i<buf.size()-1;i++){ if(buf[i]=='>'&&buf[i+1]=='>'){ filename=buf.substr(i+2,buf.size()-i-2); buf=buf.substr(0,i); filesearch=1; break; } } for(int i=1;i<buf.length();i++){
if(buf[i]=='='){ for(int j=i;j<buf.length();j++){ if(buf[j]=='\"'&×==0){ temp=j+1; times++; }else if(buf[j]=='\"'&×==1){ value=buf.substr(temp,j-temp); times++; break; } if(j==buf.length()-1){ string qwq=buf.substr(temp,j-temp); auto iter = umap.find(qwq); if(iter!=umap.end()){ value=iter->second; break; } else{ cerr<<"输入了一个不存在的变量,如果需要输入常量abcd,请用\"abcd\"包裹常量abcd :("<<endl; } } } if(times%2){ cerr<<"请确保\"abcd\"包裹常量abcd :("<<endl; } name=buf.substr(0,i); umap[name]=value; return ; } } ExpTraslate(buf); } bool isOperater(string str){ if(str=="|"||str=="&"||str=="~"){ return true; } return false; } int Precedence(char op) { if (op == '~') return 3; if (op == '&') return 2; if (op == '|') return 1; else return 0; } void ExpTraslate(string &buf){ stack<shared_ptr<Query>> qstack; stack<char> opstack; char op; vector<string> backexp; int word_start=0; bool last_right=buf[buf.size()-1]==')'; bool Had_op=0; int priority1; for(int i=0;i<buf.size();i++){ if(buf[i]=='~'||buf[i]=='|'||buf[i]=='&'||buf[i]=='('||buf[i]==')'){ Had_op=1; op=buf[i]; priority1=Precedence(op); if (opstack.empty()||op=='('){ opstack.push(op); if(i-word_start>0){ backexp.push_back(buf.substr(word_start,i-word_start)); } word_start=i+1; } else if(op ==')'){ if(i-word_start>0){ backexp.push_back(buf.substr(word_start,i-word_start)); } while(!opstack.empty()&&opstack.top()!='('){ string tempop(1,opstack.top()); opstack.pop(); backexp.push_back(tempop); } if(!opstack.empty()&&opstack.top()=='('){ opstack.pop(); } else{ cerr<<"表达式括号不匹配,请检查并重启程序 :("<<endl; exit(-1); } word_start=i+1; } else if(op=='~'||op=='|'||op=='&') { if(i-word_start>0){ backexp.push_back(buf.substr(word_start,i-word_start)); } while((!opstack.empty())&&(Precedence(opstack.top())>=priority1)){ string tempop(1,opstack.top()); opstack.pop(); backexp.push_back(tempop); } opstack.push(op); word_start=i+1; } } } if(!last_right){ backexp.push_back(buf.substr(word_start,buf.size()-word_start)); } while(!opstack.empty()){ string tempop(1,opstack.top()); opstack.pop(); backexp.push_back(tempop); } if(!Had_op){ Query onetime(IsNameReturn(buf)); if(filesearch){ OFileStream ofi(filename); cout<<onetime.rep()<<endl; onetime.eval(TextQuery(ifs->getifsteram())).write(ofi.getofsteram()); cout<<"写入成功"<<endl; }else{ cout<<onetime.rep()<<endl; onetime.eval(TextQuery(ifs->getifsteram())).print(); return; } } while(!backexp.empty()){ if(isOperater(backexp.front())) { string opstr=backexp.front(); if(opstr=="|"){ if(qstack.size()<2){ cerr<<"表达式所需要的操作数不够... :("<<endl; return ; } shared_ptr<Query> temp1=qstack.top(); qstack.pop(); shared_ptr<Query> temp2=qstack.top(); qstack.pop(); shared_ptr<Query> result=make_shared<Query>(*temp1.get()|*temp2.get()); qstack.push(result); }else if(opstr=="&"){ if(qstack.size()<2){ cerr<<"表达式所需要的操作数不够... :("<<endl; return ; } shared_ptr<Query> temp1=qstack.top(); qstack.pop(); shared_ptr<Query> temp2=qstack.top(); qstack.pop(); shared_ptr<Query> result=make_shared<Query>(*temp1.get()&*temp2.get()); qstack.push(result); }else if(opstr=="~"){ if(qstack.size()<1){ cerr<<"表达式所需要的操作数不够... :("<<endl ; return ; } shared_ptr<Query> temp=qstack.top(); qstack.pop(); shared_ptr<Query> result=make_shared<Query>(~(*temp.get())); qstack.push(result); } backexp.erase(backexp.begin()); } else{ qstack.push(make_shared<Query>(IsNameReturn(backexp.front()))); backexp.erase(backexp.begin()); } } if(qstack.size()==1){ if(filesearch){ OFileStream ofi(filename); qstack.top()->eval(TextQuery(ifs->getifsteram())).write(ofi.getofsteram()); cout<<"写入成功"<<endl; }else{ qstack.top()->eval(TextQuery(ifs->getifsteram())).print(); } }else{ cerr<<"表达式可能出现错误,无法运算出唯一结果"<<endl; exit(-1); } } void menu(){ cout<<"选择需要的功能:"<<endl; cout<<"1.文本格式化"<<endl; cout<<"2.文本查询"<<endl; cout<<"3.退出"<<endl; cout<<"Your Choice: "; }
int main() { int cho; string buf;
string newbuf; while(1){ menu(); cin>>cho; cin.ignore(); switch (cho) { case 1: cout<<"请输入读取的文件路径"<<endl; cout<<">>> "; getline(cin,buf); cout<<"请指定每行最大的字符数n :)"<<endl; cout<<">>>"; int num; cin>>num; cin.ignore(); cout<<"请指定新文件保存的路径 :)"<<endl; cout<<">>>"; getline(cin,newbuf); try{ ff=make_shared<FileFormatter>(buf,newbuf,num); ff->readFromFile(); ff->formatText(); ff->saveToFile(); }catch(string str){ cerr<<"错误的文件路径"<<buf<<"导致打开异常 :("<<endl; }catch(char ch){ cerr<<"错误的文件路径"<<buf<<"导致写入异常 :("<<endl; } break; case 2: cout<<"请输入读取的文件路径"<<endl; cout<<">>> "; getline(cin,newbuf); try{ ifs=make_shared<IFileStream>(newbuf); cout<<">>> "; getline(cin,buf); BlankFliter(buf); StringTraslate(buf); }catch(string str){ cerr<<str; } break; case 3: exit(0); break; default: cout<<"无效的选项:("<<endl; break; } } return 0; }
|