文字查詢程式–摘自c++primer

來源:互聯網
上載者:User

   本文總結摘錄了《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;     }


     

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.