文本查询程序--摘自c++primer
2013-07-08 11:37
267 查看
本文总结摘录了《c++ primer》中的一个大例子---文本查询程序。主要目的在于学习大师的编程规范。
程序要求:读取用户指定的任意文本文件,然后允许用户从该文件中查找单词。查询的结果是该单词出现的次数,并列出每次出现所在的行。如果某单词在同一行中多次出现,程序将只显示该行一次,行号按升序显示。
分析:
输入为文件名;
必须将一行作为一个整体,由行号输出所在行;
必须每一行都分解为各个单词,并记录单词所在行,对于单词的查询返回所有所在行;
此处主要考察容器的使用;
求解:
使用vector来存储整行,vector中一个元素就是一行;
使用map来以单词为关键字,以所在行的集合为卫星数据;
下面是所包含的的头文件:
下面是主要类TextQuery:
下面是辅助函数make_plural和open_file:
下面是类中的各个函数实现,此处run_query的实现采用的是作者的第二种建议,返回一对迭代器:
测试程序如下:
扩展上面的文本查询程序,要求:
查找单个单词的查询,按升序显示所有包含该单词的行;
“非”查询,使用~操作符,显示所有不匹配行;
“或”查询,使用|操作符,显示与两个查询条件中任意一个匹配的所有行;
“与”查询,使用&操作符,显示与两个查询条件都匹配的所有行;
可以组合这些操作符;
设计类如下:
我们使用时:
Query q=Query(“ ”) & Query(“ ”)|Query()
这个使用方式告诉我们,用户级代码将不能直接使用我们的继承层次,我们只能定义一个名为Query的句柄类,来隐藏继承层次。用户代码将根据句柄执行,用户代码只能间接操纵Query_base对象。
以Query q=Query("fiery") & Query("bird") |Query("wind")为例:
生成10个对象:5个Query_base对象及其相关联的句柄。
此处直接截取书中代码:
Query句柄类:
重载操作符:
输出操作符:
WordQuery类:
NotQuery类:
Binary纯虚类:
OrQuery类:
AndQuery类:
程序要求:读取用户指定的任意文本文件,然后允许用户从该文件中查找单词。查询的结果是该单词出现的次数,并列出每次出现所在的行。如果某单词在同一行中多次出现,程序将只显示该行一次,行号按升序显示。
分析:
输入为文件名;
必须将一行作为一个整体,由行号输出所在行;
必须每一行都分解为各个单词,并记录单词所在行,对于单词的查询返回所有所在行;
此处主要考察容器的使用;
求解:
使用vector来存储整行,vector中一个元素就是一行;
使用map来以单词为关键字,以所在行的集合为卫星数据;
下面是所包含的的头文件:
#include<utility> #include<set> #include<map> #include<vector> #include<string> #include<iostream> #include<fstream> #include<sstream> using namespace std;
下面是主要类TextQuery:
class TextQuery { public: //typedef to make declarations easier typedef std::vector<std::string>::size_type line_no; typedef std::set<line_no>::const_iterator set_it; /* interface: * read_file() builds internal data structures for the given file * run_query() finds the given word and returns "set" of lines on which it appears * text_line() return a requested line from the input file */ void read_file(std::ifstream& is) { store_file(is); build_map();} std::pair<set_it,set_it> run_query(const std::string& ) const; std::string text_line(line_no) const; private: //utility functions used by read_file() void store_file(std::ifstream& ); //store input file void build_map(); //associated each word with a set of line numbers; std::vector<std::string> lines_of_text; //remember the whole input file std::map<std::string, std::set<line_no> > word_map; };
下面是辅助函数make_plural和open_file:
/*此函数提供一个计数器,一个单词word和单词结束符ending, 当计数器值大于1时,返回单词的复数版本*/ std::string make_plural(size_t ctr,const std::string& word, const std::string& ending) { return (ctr==1)?word :word+ending; } //打开给定文件 std::ifstream& open_file(std::ifstream& in,const std::string& file) { in.close();//close in case it was already open in.clear();//clear any existing errors; in.open(file.c_str()); return in; }
下面是类中的各个函数实现,此处run_query的实现采用的是作者的第二种建议,返回一对迭代器:
//第一步:存储输入文件 void TextQuery::store_file(ifstream& is) { string textline; while( getline(is,textline) ) lines_of_text.push_back(textline); } //第二步:建立单词map容器 void TextQuery::build_map() { for(line_no line_num=0;line_num!=lines_of_text.size();++line_num){ istringstream line(lines_of_text[line_num]); string word; while(line>>word){ //add this line number to the set; //其中word_map[word]是一个集合,然后insert word_map[word].insert(line_num); } } } //第三步:支持查询 //返回set的一对迭代器 pair<TextQuery::set_it,TextQuery::set_it> TextQuery::run_query(const string& query_word) const { //此处需要注意不能用下标操作直接查询,以防止插入不存在的元素 map<string,set<line_no> >::const_iterator loc=word_map.find(query_word); return make_pair((loc->second).begin(),(loc->second).end()); } //第四步: 输出结果 void print_results( pair<TextQuery::set_it,TextQuery::set_it>& its, const string& sought,const TextQuery& file) { typedef set<TextQuery::line_no> line_nums; line_nums::size_type size=0; for(TextQuery::set_it beg=its.first;beg!=its.second;++beg) ++size; cout<<"\n"<<sought<<" occurs "<<size<<" "<<make_plural(size,"time","s")<<endl; line_nums::const_iterator it=its.first; for(;it!=its.second;++it){ cout<<"\t(line "<<(*it)+1<<")"<<file.text_line(*it)<<endl; } } string TextQuery::text_line(line_no line) const { if(line<lines_of_text.size()) return lines_of_text[line]; throw std::out_of_range("Line number out of range"); }
测试程序如下:
int main() { ifstream infile; string filename; if(!(cin>>filename) || !open_file(infile,filename)){ cerr<<"NO input file!"<<endl; return EXIT_FAILURE; } TextQuery tq; tq.read_file(infile); while(true){ cout<<"enter word to look for, or q to quit: "; string s;cin>>s; if(!cin || s=="q") break; pair<TextQuery::set_it,TextQuery::set_it> its=tq.run_query(s); print_results(its,s,tq); } return 0; }
扩展上面的文本查询程序,要求:
查找单个单词的查询,按升序显示所有包含该单词的行;
“非”查询,使用~操作符,显示所有不匹配行;
“或”查询,使用|操作符,显示与两个查询条件中任意一个匹配的所有行;
“与”查询,使用&操作符,显示与两个查询条件都匹配的所有行;
可以组合这些操作符;
设计类如下:
我们使用时:
Query q=Query(“ ”) & Query(“ ”)|Query()
这个使用方式告诉我们,用户级代码将不能直接使用我们的继承层次,我们只能定义一个名为Query的句柄类,来隐藏继承层次。用户代码将根据句柄执行,用户代码只能间接操纵Query_base对象。
以Query q=Query("fiery") & Query("bird") |Query("wind")为例:
生成10个对象:5个Query_base对象及其相关联的句柄。
此处直接截取书中代码:
// private, abstract class acts as a base class for concrete query types class Query_base { friend class Query; protected: typedef TextQuery::line_no line_no; virtual ~Query_base() { } private: // eval returns the |set| of lines that this Query matches virtual std::set<line_no> eval(const TextQuery&) const = 0; // display prints the query virtual std::ostream& display(std::ostream& = std::cout) const = 0; };
Query句柄类:
// handle class to manage the Query_base inheritance hierarchy class Query { // these operators need access to the Query_base* constructor friend Query operator~(const Query &); friend Query operator|(const Query&, const Query&); friend Query operator&(const Query&, const Query&); public: Query(const std::string&); // builds a new WordQuery // copy control to manage pointers and use counting Query(const Query &c): q(c.q), use(c.use) { ++*use; } ~Query() { decr_use(); } Query& operator=(const Query&); // interface functions: will call corresponding Query_base operations std::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 Query operator&(const Query &lhs, const Query &rhs) { return new AndQuery(lhs, rhs); } inline Query operator|(const Query &lhs, const Query &rhs) { return new OrQuery(lhs, rhs); } inline Query operator~(const Query &oper) { return new NotQuery(oper); }
输出操作符:
inline std::ostream& operator<<(std::ostream &os, const Query &q) { return q.display(os); }
WordQuery类:
class WordQuery: public Query_base { friend class Query; // Query uses the WordQuery constructor WordQuery(const std::string &s): query_word(s) { } // concrete class: WordQuery defines all inherited pure virtual functions std::set<line_no> eval(const TextQuery &t) const { return t.run_query(query_word); } std::ostream& display (std::ostream &os) const { return os << query_word; } std::string query_word; // word for which to search };
NotQuery类:
class NotQuery: public Query_base { friend Query operator~(const Query &); NotQuery(Query q): query(q) { } // concrete class: NotQuery defines all inherited pure virtual functions std::set<line_no> eval(const TextQuery&) const; std::ostream& display(std::ostream &os) const { return os << "~(" << query << ")"; } const Query query; }; set<TextQuery::line_no> NotQuery::eval(const TextQuery& file) const { // virtual call through the Query handle to eval set<TextQuery::line_no> has_val = query.eval(file); set<line_no> ret_lines; // for each line in the input file, check whether that line is in has_val // if not, add that line number to ret_lines for (TextQuery::line_no n = 0; n != file.size(); ++n) if (has_val.find(n) == has_val.end()) ret_lines.insert(n); return ret_lines; }
Binary纯虚类:
class BinaryQuery: public Query_base { protected: BinaryQuery(Query left, Query right, std::string op): lhs(left), rhs(right), oper(op) { } // abstract class: BinaryQuery doesn't define eval std::ostream& display(std::ostream &os) const { return os << "(" << lhs << " " << oper << " " << rhs << ")"; } const Query lhs, rhs; // right- and left-hand operands const std::string oper; // name of the operator };
OrQuery类:
class OrQuery: public BinaryQuery { friend Query operator|(const Query&, const Query&); OrQuery(Query left, Query right): BinaryQuery(left, right, "|") { } // concrete class: OrQuery inherits display and defines remaining pure virtual std::set<line_no> eval(const TextQuery&) const; }; set<TextQuery::line_no> OrQuery::eval(const TextQuery& file) const { // virtual calls through the Query handle to get result sets for the operands set<line_no> right = rhs.eval(file), ret_lines = lhs.eval(file); // destination to hold results // inserts the lines from right that aren't already in ret_lines ret_lines.insert(right.begin(), right.end()); return ret_lines; }
AndQuery类:
class AndQuery: public BinaryQuery { friend Query operator&(const Query&, const Query&); AndQuery (Query left, Query right): BinaryQuery(left, right, "&") { } // concrete class: And Query inherits display and defines remaining pure virtual std::set<line_no> eval(const TextQuery&) const; }; set<TextQuery::line_no> AndQuery::eval(const TextQuery& file) const { // virtual calls through the Query handle to get result sets for the operands set<line_no> left = lhs.eval(file), right = rhs.eval(file); set<line_no> ret_lines; // destination to hold results // writes intersection of two ranges to a destination iterator // destination iterator in this call adds elements to ret set_intersection(left.begin(), left.end(), right.begin(), right.end(), inserter(ret_lines, ret_lines.begin())); return ret_lines; }
相关文章推荐
- C++PRIMER-5TH 15.9 文本查询程序再探 练习理解(二)
- 【足迹C++primer】56、文本查询程序
- C++primer 文本查询程序
- C++文本查询程序 C++Primer练习12.28 使用vector,map,set容器保存来自文件的数据并生成查询结果
- C++文本查询程序 定义类管理数据 用智能指针 C++Primer练习12.30
- C++文本查询程序 定义类管理数据 用引用共享数据 不用智能指针 C++Primer练习12.27
- C++文本查询程序 不要定义类和智能指针管理数据 C++Primer练习12.28 使用vector,map,set容器保存来自文件的数据并生成查询结果
- C++文本查询程序 定义类管理数据 用引用共享数据 不用智能指针 C++Primer练习12.27
- C++文本查询程序 定义类管理数据 用智能指针 C++Primer练习12.30
- C++文本查询程序 定义类管理数据 用智能指针 用StrBlob代替vector<string> C++Primer 练习12.32
- C++文本查询程序 C++Primer练习12.28 使用vector,map,set容器保存来自文件的数据并生成查询结果
- C++文本查询程序 定义类管理数据 用智能指针 C++Primer练习12.30
- C++文本查询程序 定义类管理数据 用引用共享数据 不用智能指针 C++Primer练习12.27
- C++文本查询程序 不要定义类和智能指针管理数据 C++Primer练习12.28 使用vector,map,set容器保存来自文件的数据并生成查询结果
- C++文本查询程序 定义类管理数据 用引用共享数据 不用智能指针 C++Primer练习12.27
- C++文本查询程序 定义类管理数据 用智能指针 C++Primer练习12.30
- C++文本查询程序 定义类管理数据 用智能指针 用StrBlob代替vector<string> C++Primer 练习12.32
- C++Primer 【笔记】文本查询程序 TextQuery
- C++文本查询程序 C++Primer练习12.28 使用vector,map,set容器保存来自文件的数据并生成查询结果
- C++文本查询程序 定义类管理数据 用智能指针 C++Primer练习12.30