文本查询程序--摘自c++primer

   本文总结摘录了《c++  primer》中的一个大例子---文本查询程序。主要目的在于学习大师的编程规范。


   程序要求:读取用户指定的任意文本文件,然后允许用户从该文件中查找单词。查询的结果是该单词出现的次数,并列出每次出现所在的行。如果某单词在同一行中多次出现,程序将只显示该行一次,行号按升序显示。


分析:

  1. 输入为文件名;
  2. 必须将一行作为一个整体,由行号输出所在行;
  3. 必须每一行都分解为各个单词,并记录单词所在行,对于单词的查询返回所有所在行;
  4. 此处主要考察容器的使用;


求解

  1. 使用vector来存储整行,vector中一个元素就是一行;
  2. 使用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; 
 }

扩展上面的文本查询程序,要求:

  1. 查找单个单词的查询,按升序显示所有包含该单词的行;
  2. “非”查询,使用~操作符,显示所有不匹配行;
  3. “或”查询,使用|操作符,显示与两个查询条件中任意一个匹配的所有行;
  4. “与”查询,使用&操作符,显示与两个查询条件都匹配的所有行;
  5. 可以组合这些操作符;

设计类如下:


我们使用时:

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;
     }








     




评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值