C++ Primer(第5版) 练习 12.32
练习 12.32 重写TextQuery和QueryResult类,用StrBlob代替vector保存输入文件。
环境:Linux Ubuntu(云服务器)
工具:vim
代码块
/*************************************************************************> File Name: ex12.32.cpp> Author: > Mail: > Created Time: Mon 22 Apr 2024 08:21:38 AM CST************************************************************************/#include<iostream>
#include<fstream>
#include<sstream>
#include<vector>
#include<map>
#include<set>
#include<memory>
using namespace std;class StrBlobPtr;class StrBlob{friend class StrBlobPtr;public:typedef vector<string>::size_type size_type;StrBlob(): data(make_shared<vector<string>>()) {}StrBlob(initializer_list<string> il): data(make_shared<vector<string>>(il)) {}size_type size() const { return data->size(); }bool empty() const { return data->empty(); }void push_back(const string &t) { data->push_back(t); }void pop_back();string &front();string &back();string &front() const;string &back() const;StrBlobPtr begin();StrBlobPtr end();private:shared_ptr<vector<string>> data;void check(size_type i, const string &msg) const;
};void StrBlob::pop_back(){check(0, "pop_back on empty StrBlob");data->pop_back();
}string& StrBlob::front(){check(0, "front on empty StrBlob");return data->front();
}string& StrBlob::back(){check(0, "back on empty StrBlob");return data->back();
}string& StrBlob::front() const{check(0, "front on empty StrBlob");return data->front();
}string& StrBlob::back() const{check(0, "back on empty StrBlob");return data->back();
}void StrBlob::check(size_type i, const string &msg) const{if(i >= data->size()){throw out_of_range(msg);}
}class StrBlobPtr{public:StrBlobPtr(): curr(0) {}StrBlobPtr(StrBlob &s, size_t sz = 0): wptr(s.data), curr(sz) {}string &deref() const;StrBlobPtr &incr();StrBlobPtr &add(StrBlob::size_type num);private:shared_ptr<vector<string>> check(size_t, const string &) const;weak_ptr<vector<string>> wptr;size_t curr;
};shared_ptr<vector<string>> StrBlobPtr::check(size_t i, const string &msg) const {auto ret = wptr.lock();if(!ret){throw runtime_error("unbound StrBlobPtr");}if(i >= ret->size()){throw out_of_range(msg);}return ret;
}string &StrBlobPtr::deref() const{auto p = check(curr, "dereference past end");return (*p)[curr];
}StrBlobPtr &StrBlobPtr::incr(){check(curr, "increment past end of StrBlobPtr");++curr;return *this;
}StrBlobPtr &StrBlobPtr::add(StrBlob::size_type num){check(curr + num, "increment past end of StrBlobPtr");curr += num;return *this;
}StrBlobPtr StrBlob::begin(){return StrBlobPtr(*this);
}StrBlobPtr StrBlob::end(){auto ret = StrBlobPtr(*this, data->size());return ret;
}string make_plural(size_t ctr, string &word, const string &ending = "s"){int size = word.size();if(ctr <= 1){return word;}else{if(word[size-1] == 's' || word[size-1] == 'x' || (word[size-1] == 'h' && word[size-2] == 's') || (word[size-1] == 'h' && word[size-2] == 'c')){return word + "e" + ending;}else if(word[size-1] == 'y' && (word[size-2] != 'a' && word[size-2] != 'e' && word[size-2] != 'i' && word[size-2] != 'o' && word[size-2] != 'u')){word[size-1] = 'i';return word + "e" + ending;}else if((word[size-3] != 'a' && word[size-3] != 'e' && word[size-3] != 'i' && word[size-3] != 'o' && word[size-3] != 'u') && (word[size-2] != 'a' && word[size-2] != 'e' && word[size-2] != 'i' && word[size-2] != 'o' && word[size-2] != 'u')){if(word[size-1] == 'f'){word[size-1] = 'v';return word + ending;}else if(word[size-2] == 'f' && word[size-1] == 'e'){word[size-2] = 'v';return word + "e" + ending;}}else{return word + ending;}}return word;
}class QueryResult;class TextQuery{public:using line_no = StrBlob::size_type;TextQuery(ifstream &is);QueryResult query(const string &s) const;private:shared_ptr<StrBlob> file;map<string, shared_ptr<set<line_no>>> wm;
};TextQuery::TextQuery(ifstream &is): file(new StrBlob){string text;while(getline(is, text)){file->push_back(text);int n = file->size() - 1;istringstream line(text);string word;while(line>>word){auto &lines = wm[word];if(!lines){lines.reset(new set<line_no>);lines->insert(n);}}}
}class QueryResult{friend ostream &print(ostream &, const QueryResult &);public:QueryResult(string s, shared_ptr<set<TextQuery::line_no>> p, shared_ptr<StrBlob> f): sought(s), lines(p), file(f) {}private:string sought;shared_ptr<set<TextQuery::line_no>> lines;shared_ptr<StrBlob> file;
};QueryResult TextQuery::query(const string &sought) const{static shared_ptr<set<line_no>> nodata(new set<line_no>);auto loc = wm.find(sought);if(loc == wm.end()){return QueryResult(sought, nodata, file);}else{return QueryResult(sought, loc->second, file);}
}ostream &print(ostream &os, const QueryResult &qr){string na = "time";os<<qr.sought<<" occurs "<<qr.lines->size()<<" "<<make_plural(qr.lines->size(), na)<<endl;for(auto num : *qr.lines){os<<"\t(line "<<num + 1<<") "<<qr.file->begin().add(num).deref()<<endl;}return os;
}void runQueries(ifstream &infile){TextQuery tq(infile);while(true){cout<<"enter word to look for, or q to quit: ";string s;if(!(cin>>s) || s == "q"){break;}print(cout, tq.query(s))<<endl;}
}int main(){ifstream file("12.27.txt");runQueries(file);return 0;
}