C++ Primer第四版 15.9 再谈文本查询 程序实现
编程过程中发现书本中的示例程序并不完全,某些地方存在错误,现已改正并添加少许注释..
1 #include<iostream>
2 #include<fstream> 3 #include<sstream> 4 #include<vector> 5 #include<map> 6 #include<set> 7 #include<algorithm> 8 #include<stdexcept> 9 using namespace std; 10 11 class TextQuery{ 12 public: 13 typedef vector<string>::size_type line_no; 14 void read_file(ifstream &is){ 15 store_file(is); //读取并保存输入文件 16 build_map(); //创建关联单词与行号的容器 17 } 18 set<line_no> run_query(const string&) const; //根据输入的字符串,在map中查找,返回一个set集合,里面包含了该字符串在文件的所有行号 19 line_no size() const {return lines_of_text.size();} 20 string text_line(line_no) const; //以行号为输入参数,返回该行的字符串 21 private: 22 void store_file(ifstream&); 23 void build_map(); 24 vector<string> lines_of_text; //容器lines_of_text保存文本的副本 25 map<string,set<line_no> > word_map; //first存的是单词,second存的是set集合,里面是单词所在的所有行号 26 }; 27 28 29 void TextQuery::store_file(ifstream &is) //将文本文件读入内存保存在lines_of_text容器中 30 { 31 string textline; 32 while(getline(is,textline)) //逐行存入vector 33 lines_of_text.push_back(textline); 34 } 35 36 37 void::TextQuery::build_map() 38 { 39 for(line_no line_num = 0; line_num != lines_of_text.size(); ++line_num) 40 { 41 istringstream line(lines_of_text[line_num]); //将vector中的文件内容逐行提取出来 42 string word; 43 while(line >> word) //将每行字符串分解成单词,插入到map中,键是单词,值是行号 44 word_map[word].insert(line_num); //*********word_map[word]返回的是右值,是一个set对象,insert()函数是set的函数******** 45 } 46 } 47 48 49 set<TextQuery::line_no> TextQuery::run_query(const string &query_word) const 50 { 51 map<string,set<line_no> >::const_iterator loc = word_map.find(query_word); 52 if(loc == word_map.end()) 53 return set<line_no>(); 54 else 55 return loc->second; 56 } 57 58 59 string TextQuery::text_line(line_no line) const 60 { 61 if(line < lines_of_text.size()) 62 return lines_of_text[line]; 63 throw std::out_of_range("line number out of range"); 64 } 65 66 67 string make_plural(size_t ctr, const string &word, const string &ending) 68 { 69 return (ctr == 1) ? word : word + ending; 70 } 71 72 73 void print_results(const set<TextQuery::line_no>& locs ,const string& sought, const TextQuery &file) 74 { 75 typedef set<TextQuery::line_no> line_nums; 76 line_nums::size_type size = locs.size(); //set集合的大小即是该单词出现的次数 77 cout<<"\n"<<sought<<" occurs "<<size<<" "<<make_plural(size,"time","s") <<endl; 78 line_nums::const_iterator it = locs.begin(); 79 for(;it != locs.end(); ++it) 80 cout<<"\t(line"<<(*it)+1<<")"<<file.text_line(*it)<<endl; //原行号从0开始,+1进行修正 81 } 82 83 84 class Query_base{ 85 friend class Query; //接口只提供给Query使用,用户和派生类只能通过Query句柄使用Query_base类 86 protected: 87 typedef TextQuery::line_no line_no; 88 virtual ~Query_base(){} 89 private: 90 //eval returns the |set| of lines that this Query matches 91 virtual std::set<line_no> eval(const TextQuery&) const = 0; 92 //display prints the query 93 virtual std::ostream& display(std::ostream& = std::cout) const = 0; 94 }; 95 96 class WordQuery: public Query_base{ 97 friend class Query; 98 WordQuery(const std::string &s): query_word(s) {} 99 std::set<line_no> eval(const TextQuery &t) const {return t.run_query(query_word);} 100 std::ostream& display(std::ostream &os) const {return os<<query_word;} 101 std::string query_word; 102 }; 103 104 class Query{ 105 friend Query operator~(const Query&); 106 friend Query operator|(const Query&, const Query&); 107 friend Query operator&(const Query&, const Query&); 108 109 public: 110 Query(const std::string& word): q(new WordQuery(word)), use(new std::size_t(1)) { } 111 Query(const Query &c): q(c.q), use(c.use) {++*use;} 112 ~Query() {decr_use();} 113 114 Query& operator=(const Query&); 115 std::set<TextQuery::line_no> eval(const TextQuery &t) const {return q->eval(t);} 116 std::ostream &display(std::ostream &os) const {return q->display(os);} 117 118 private: 119 Query(Query_base * query): q(query), use(new std::size_t(1)) {} //我们不希望普通用户代码定义Query_base对象。因为是private权限,操作符必须是友元 120 Query_base * q; 121 std::size_t *use; 122 void decr_use() 123 { 124 if(--*use == 0) 125 { 126 delete q; 127 delete use; 128 } 129 } 130 }; 131 132 inline std::ostream& operator<<(std::ostream &os, const Query &q) 133 { 134 return q.display(os); 135 } 136 137 class NotQuery: public Query_base{ 138 friend Query operator~(const Query&); 139 NotQuery(Query q): query(q){} 140 std::set<line_no> eval(const TextQuery &) const; 141 std::ostream& display(std::ostream &os) const {return os<<"~("<<query<<")";} 142 const Query query; //why const? 143 }; 144 145 class BinaryQuery: public Query_base{ //未实现eval函数,因此是一个抽象类 146 protected: 147 BinaryQuery(Query left, Query right, std::string op): lhs(left), rhs(right), oper(op) {} 148 std::ostream& display(std::ostream &os) const {return os<<"("<<lhs<<" "<<oper<<" "<<rhs<<")";} 149 const Query lhs, rhs; 150 const std::string oper; 151 }; 152 153 class AndQuery: public BinaryQuery{ 154 friend Query operator&(const Query&, const Query &); 155 AndQuery(Query left, Query right): BinaryQuery(left, right, "&") {} 156 std::set<line_no> eval(const TextQuery&) const; 157 }; 158 159 160 class OrQuery: public BinaryQuery{ 161 friend Query operator|(const Query&, const Query&); 162 OrQuery(Query left, Query right): BinaryQuery(left, right, "|") {} 163 std::set<line_no> eval(const TextQuery&) const; 164 }; 165 166 set<TextQuery::line_no> OrQuery::eval(const TextQuery& file)const 167 { 168 set<line_no> right = rhs.eval(file), 169 ret_lines = lhs.eval(file); 170 ret_lines.insert(right.begin(), right.end()); 171 return ret_lines; 172 } 173 174 set<TextQuery::line_no> AndQuery::eval(const TextQuery& file) const 175 { 176 set<line_no> left = lhs.eval(file), 177 right = rhs.eval(file); 178 set<line_no> ret_lines; 179 set_intersection(left.begin(), left.end(), right.begin(), right.end(), inserter(ret_lines, ret_lines.begin())); //调用STL库函数进行容器的交集操作 180 return ret_lines; 181 } 182 183 set<TextQuery::line_no> NotQuery::eval(const TextQuery& file) const //const引用的对象,只能访问对象的const成员,所以size函数必须是const类型的 184 { 185 set<TextQuery::line_no> has_val = query.eval(file); 186 set<line_no> ret_lines; 187 for(TextQuery::line_no n = 0; n != file.size(); ++n) 188 if(has_val.find(n) == has_val.end()) 189 ret_lines.insert(n); 190 return ret_lines; 191 } 192 193 194 inline Query operator~(const Query &oper) 195 { 196 return new NotQuery(oper); //等价于Query_base * tmp = new NotQuery(oper); return Query(tmp); 197 } 198 199 inline Query operator|(const Query &lhs, const Query &rhs) 200 { 201 return new OrQuery(lhs,rhs); 202 } 203 204 inline Query operator&(const Query &lhs, const Query &rhs) 205 { 206 return new AndQuery(rhs,lhs); 207 } 208 209 210 int main() 211 { 212 ifstream infile; 213 infile.open("C:\\1.txt"); 214 if(!infile) 215 { 216 std::cout<<"Cannot open file"<<std::endl; 217 exit(1); 218 } 219 TextQuery tq; 220 tq.read_file(infile); 221 222 /* 该处填写要查找的内容逻辑表达式 */ 223 //set<TextQuery::line_no> locs = tq.run_query(s); 224 //Query q = ~(Query("today") & Query("API")); 225 Query q = Query("today is") & Query("and"); 226 set<TextQuery::line_no> locs = q.eval(tq); 227 string s = ""; 228 print_results(locs,s,tq); 229 return 0; 230 }
当然要想实现作者开头所展示的实时查询功能,还要下不少功夫,有能力的童鞋自己实现字符串的解析吧-_-