C++ primer 文本查询程序,面向对象的实现,有很多值得学习的知识点。
整理了代码如下,使用VS2010,g++编译通过:
#include <map>
#include <set>
#include <vector>
#include <string>
#include <algorithm>
#include <iterator>
#include <istream>
#include <sstream> //istringstream
using std::istringstream;
using std::map;
using std::set;
using std::vector;
using std::string;
#include <iostream>
using std::ostream;
using std::cout;
using std::cin;
using std::cerr;
using std::endl;
#include <fstream>
using std::getline;
using std::ifstream;
/**
* @brief The TextQuery class
*/
class TextQuery
{
public:
typedef vector<string>::size_type line_no;
void read_file(std::ifstream &is)
{
store_file(is);
build_map();
}
set<line_no> run_query(string &) const;
string text_line(line_no) const;
line_no size() const //the total lines num
{
return lines_of_text.size();
}
private:
void store_file(std::ifstream &);
void build_map();
vector<string> lines_of_text;
map<string,set<line_no> > word_map;
};
set<TextQuery::line_no> TextQuery::run_query(string &query_word) const
{
map<string,set<line_no> >::const_iterator iter = word_map.find(query_word);
if ( iter != word_map.end() )
{
return iter->second;
}
return set<line_no>();
}
string TextQuery::text_line(line_no line) const
{
return lines_of_text[line];
}
void TextQuery::store_file(std::ifstream &is)
{
string textline;
while ( getline(is,textline) )
{
lines_of_text.push_back(textline);
}
}
void TextQuery::build_map()
{
line_no line_num = 0;
for ( ; line_num != lines_of_text.size() ; line_num++ )
{
istringstream line(lines_of_text[line_num]);
string word;
while ( line>>word )
{
word_map[word].insert(line_num);
}
}
}
/**
* @brief The Query_base class
*/
class Query_base
{
friend class Query;
protected:
virtual ~Query_base()
{
}
private:
virtual set<TextQuery::line_no> eval(const TextQuery &) const = 0;
virtual ostream & display(ostream &os = std::cout ) const = 0;
};
class Query
{
friend Query operator~(const Query &);
friend Query operator|(const Query &,const Query &);
friend Query operator&(const Query &,const Query &);
public:
Query(const string &word); //builds a new wordQuery
//copy control to manage pointers and counting
Query(const Query &c):q(c.q),use(c.use)
{
++*use;
}
~Query()
{
decr_use();
}
Query &operator=(const Query &query)
{
q = query.q;
++*use;
return *this;
}
set<TextQuery::line_no> eval(const TextQuery &t) const
{
return q->eval(t);
}
std::ostream &display(std::ostream &os) const
{
return q->display(os);
}
private:
Query(Query_base *query):q(query),use(new std::size_t(1))
{
}
Query_base *q;
std::size_t *use;
void decr_use()
{
if ( --*use == 0 )
{
delete q;
delete use;
}
}
};
inline ostream & operator<<(ostream &os,const Query &query) //why needed const for Query &??
{
query.display(os);
return os;
}
/**
* @brief The WordQuery class
*/
class WordQuery : public Query_base
{
friend class Query;
WordQuery(const string &s):query_word(s)
{
}
set<TextQuery::line_no> eval(const TextQuery &t) const
{
return t.run_query((string &)query_word); //why needed const?
}
ostream & display(ostream &os = std::cout ) const;
string query_word;
};
ostream & WordQuery::display(ostream &os) const
{
return os<<query_word;
}
//Query constructor!! we can't define before the WordQuery class finish
Query::Query(const string &word) //builds a new wordQuery
{
q = new WordQuery(word);
use = new std::size_t(1);
}
/**
* @brief The NotQuery class
*/
class NotQuery :public Query_base
{
friend Query operator~(const Query &);
NotQuery(const Query &q):query(q)
{
}
set<TextQuery::line_no> eval(const TextQuery &) const; //becareful the last const,if you forget to add it in the implement,it overloads and bug
ostream & display(ostream &os) const;
const Query query; //const is ok,when you initianize in the constructor
};
set<TextQuery::line_no> NotQuery::eval(const TextQuery &file) const
{
set<TextQuery::line_no> has_val = query.eval(file);
set<TextQuery::line_no> ret_lines;
TextQuery::line_no n = 0;
for ( ; n != file.size() ; ++n )
{
if ( has_val.find(n) == has_val.end() )
{
ret_lines.insert(n);
}
}
return ret_lines;
}
ostream & NotQuery::display(ostream &os) const
{
return os<<"~("<<query<<")";
}
/**
* @brief The BinaryQuery class
*/
class BinaryQuery : public Query_base
{
protected: //can't not be private
BinaryQuery(Query left,Query right,string op):lhs(left),rhs(right),oper(op)
{
}
ostream & display(ostream &os = std::cout ) const
{
return os<<"("<<lhs<<" "<<oper<<" "<<rhs<<")";
}
Query lhs,rhs;
string oper; //& |
};
/**
* @brief The AndQuery class
*/
class AndQuery : public BinaryQuery
{
friend Query operator&(const Query &,const Query &);
AndQuery(const Query &left,const Query &right):BinaryQuery(left,right,"&")
{
}
set<TextQuery::line_no> eval(const TextQuery &) const;
};
set<TextQuery::line_no> AndQuery::eval(const TextQuery &file) const
{
set<TextQuery::line_no> left = lhs.eval(file),
right = rhs.eval(file);
set<TextQuery::line_no> ret_lines;
set_intersection(left.begin(),left.end(),right.begin(),right.end(),inserter(ret_lines,ret_lines.begin()));
return ret_lines;
}
/**
* @brief The OrQuery class
*/
class OrQuery : public BinaryQuery
{
friend Query operator|(const Query &,const Query &);
OrQuery(const Query lq,const Query rq):BinaryQuery(lq,rq,"|")
{
}
set<TextQuery::line_no> eval(const TextQuery &) const;
};
set<TextQuery::line_no> OrQuery::eval(const TextQuery &file) const
{
set<TextQuery::line_no> right = lhs.eval(file),
ret_lines = rhs.eval(file);
ret_lines.insert(right.begin(),right.end());
return ret_lines;
}
Query operator~(const Query &query)
{
return new NotQuery(query);
}
Query operator|(const Query &lhr,const Query &rhr)
{
return new OrQuery(lhr,rhr);
}
Query operator&(const Query &lhr,const Query &rhr)
{
return new AndQuery(lhr,rhr);
}
void print_result(const set<TextQuery::line_no> &locs,const TextQuery &file)
{
typedef set<TextQuery::line_no> line_nums;
line_nums::const_iterator iter = locs.begin();
for( ; iter != locs.end() ; iter++ )
{
cout<<"\tline"<<(*iter)+1<<")"<<file.text_line(*iter)<<endl;
}
}
bool open_file(ifstream &is,char *filename)
{
if ( is.is_open() )
{
is.close();
}
is.open(filename);
if ( is.is_open() == true )
{
return true;
}
return false;
}
int main()
{
ifstream infile;
if ( !open_file(infile,(char *)"TextQuery.txt"))
{
cerr<<"No input file!"<<endl;
return -1;
}
TextQuery tq;
tq.read_file(infile);
//Query q = Query("I") & Query("like") | Query("you");
//Query q = Query("you") & Query("like");
Query q = Query("I") & Query("like");
set<TextQuery::line_no> res = q.eval(tq);
q.display(cout);
cout<<endl;
print_result(res,tq);
}
测试文件为 TextQuery.txt ,内容如下,
I like you and hope you like me!
But you don't like me,I know that,I am so sad!
One day,will you like me?