#include <iostream>
using std::cin; using std::cout; using std::endl; using std::cerr;
using std::ostream;
#include <fstream>
using std::ifstream;
#include <sstream>
using std::istringstream;
#include <string>
using std::string;
#include <vector>
using std::vector;
#include <map>
using std::map;
#include <set>
using std::set;
#include <memory>
using std::shared_ptr; using std::make_shared;
#include <algorithm>
using std::set_intersection;
#include <iterator>
using std::inserter;
using line_no = vector<string>::size_type; //将行号定义为vector<string>的索引
class QueryResult;
class TextQuery{
public:
using line_no = vector<string>::size_type; //将行号定义为vector<string>的索引
TextQuery(ifstream &);
QueryResult query(const string &) const ;
private:
shared_ptr<vector<string>> file; //vector<string>保存整个输入文件的拷贝,每行为vector中的一个元素
map<string, shared_ptr<set<line_no>>> wm; //将每个单词与其在输入文本的行号的set关联起来
};
class QueryResult{
friend ostream &print(ostream &, const QueryResult &);
public:
QueryResult(string s, shared_ptr<set<line_no>> p, shared_ptr<vector<string>> f):
sought(s), lines(p), file(f) {};
set<line_no>::iterator begin() { return lines->begin();}
set<line_no>::iterator end() { return lines->end();}
shared_ptr<vector<string>> get_file() { return file;}
private:
string sought; //要查询的单词
shared_ptr<set<line_no>> lines; //单词出现的行号
shared_ptr<vector<string>> file; //输入文件
};
string make_plural(int cnt, const string &s, const string &pf){
return cnt > 1 ? s + pf : s;
}
//读取输入文件并建立单词到行号的映射
TextQuery::TextQuery(ifstream &is) : file(new vector<string>) {
string text;
while (getline(is, text)){ //对文件中的每一行
file->push_back(text); //保存此行文本
int n = file->size() - 1; //当前行号
istringstream line(text); //将行文本分解为单词
string word;
while (line >> word){ //对行中每个单词
//如果单词不在wm中,以之为下标在wm中添加一项
auto &lines = wm[word]; //lines是一个shared_ptr
if (!lines) //在第一次遇到此单词时,此指针为空
lines.reset(new set<line_no>); //分配一个新的set;
lines->insert(n); //将此行号插入set中
}
}
}
QueryResult TextQuery::query(const string &sought) const {
//如果未找到sought,将返回一个指向此set的指针
static shared_ptr<set<line_no>> nodata(new set<line_no>);
//使用find而不是下标运算符来找单词,避免将单词添加到wm中
auto loc = wm.find(sought);
if (loc == wm.end())
return QueryResult(sought, nodata, file); //未找到
else
return QueryResult(sought, loc->second, file);
}
void runQueries(ifstream &infile){
//infile是一个ifstream,指向我们要处理的文件
TextQuery tq(infile); //保存文件并建立查询map
//与用户交互,提示用户输入要查询的单词,完成查询并打印结果
while (true){
cout << "Enter word to look for, or q to quit: ";
string s;
//若遇到文件尾或用户输入了'q'时循环终止
if (!(cin >> s) || s == "q") break;
//指向查询并打印结果
print(cout, tq.query(s)) << endl;
}
}
ostream &print(ostream &os, const QueryResult &qr){
//如果找到了单词,打印出现次数和所有出现的位置
os << qr.sought << " occurs " << qr.lines->size() << " "
<< make_plural(qr.lines->size(), "time", "s") << endl;
//打印单词出现的每一行
for (auto num : *qr.lines){ //对set中的每个单词
//避免行号从0开始给用户困惑
os << "\t(lines " << num + 1 << ") " << *(qr.file->begin() + num) << endl;
}
return os;
}
//这是一个抽象基类,具体的查询类型从中派生,所有成员都是private的
class Query_base{
friend class Query;
protected:
using line_no = TextQuery::line_no ; //用于eval函数
virtual ~Query_base() = default;
private:
//eval返回与当前Query匹配的QueryResult
virtual QueryResult eval(const TextQuery &) const = 0;
//rep是表示查询的一个string
virtual string rep() const = 0;
};
//这是一个管理Query_base继承体系的接口类
class Query{
//这些运算符需要访问接受shared_ptr的构造函数,而该函数是私有的
friend Query operator~(const Query &);
friend Query operator|(const Query &, const Query &);
friend Query operator&(const Query &, const Query &);
public:
Query(const string &); //构建一个新的WordQuery
//接口函数:调用对应的Query_base操作
QueryResult eval(const TextQuery &t) const { return q->eval(t);}
string rep() const { cout << "Query::rep" << endl; return q->rep();}
private:
Query(shared_ptr<Query_base> query) : q(query) {}
shared_ptr<Query_base> q;
};
inline
ostream &operator<<(ostream &os, const Query &query){
//Query::rep通过它的Query_base指针对rep()进行了虚调用
return os << query.rep();
}
class WordQuery : public Query_base{
friend class Query; //Query使用WordQuery的构造函数
WordQuery(const string &s) : query_word(s) {cout << "WordQuery::WordQuery(const string &)" << endl;}
//具体的类:WordQuery将定义所有继承而来的纯虚函数
QueryResult eval(const TextQuery &t) const { return t.query(query_word);}
string rep() const { cout << "WordQuery::rep" << endl; return query_word;}
string query_word; //要查找的单词
};
inline
Query::Query(const string &s) : q(new WordQuery(s)) { cout << "Query::Query(const string &)" << endl;}
class NotQuery : public Query_base{
friend Query operator~(const Query &);
NotQuery(const Query &q) : query(q) {cout << "NotQuery::NotQuery(const string &)" << endl;};
//具体的类:NotQuery将定义所有继承而来的纯虚函数
string rep() const {cout << "NotQuery::rep" << endl; return "~(" + query.rep() + ")";}
QueryResult eval(const TextQuery &) const ;
Query query;
};
inline
Query operator~(const Query &operand){
return shared_ptr<Query_base>(new NotQuery(operand));
}
//返回运算对象的结果set中不存在的行
QueryResult
NotQuery::eval(const TextQuery &text) const {
//通过Query运算对象对eval进行虚调用
auto result = query.eval(text);
//开始时结果set为空
auto ret_lines = make_shared<set<line_no>>();
//必须在运算对象出现的所有行中进行迭代
auto beg = result.begin(), end = result.end();
//对于输入文件的每一行,如果该行不在result当中,则将其添加到ret_lines
auto sz = result.get_file()->size();
for (size_t n = 0; n != sz; ++n){
//如果我们还没有处理完result的所有行
//检查当前行是否存在
if (beg == end || *beg != n) {
ret_lines->insert(n); //如果不在result当中,添加这一行
} else if (beg != end)
++beg; //否则继续获取result的下一行(如果有必要的话)
}
return QueryResult(rep(), ret_lines, result.get_file());
}
class BinaryQuery : public Query_base{
protected:
BinaryQuery(const Query &l, const Query &r, const string &s) : lhs(l), rhs(r), opSym(s) {
cout << "BinaryQuery::BinaryQuery(const Query &, const Query &, const string &)" << endl;
};
//抽象类:BinaryQuery不定义eval
string rep() const {
cout << "BinaryQuery::rep" << endl;
return "(" + lhs.rep() + " " + opSym + " " + rhs.rep() + ")";}
Query lhs, rhs; //左侧和右侧运算对象
string opSym; //运算符的名字
};
class AndQuery : public BinaryQuery{
friend Query operator&(const Query &, const Query &);
AndQuery(const Query &left, const Query &right) : BinaryQuery(left, right, "&") {
cout << "AndQuery::AndQuery(const Query &, const Query &)" << endl;
};
//具体的类:AndQuery继承了rep并且定义了其它纯虚函数
QueryResult eval(const TextQuery &) const;
};
inline
Query operator&(const Query &lhs, const Query &rhs){
return shared_ptr<Query_base>(new AndQuery(lhs, rhs));
}
//返回运算对象查询结果set的交集
QueryResult
AndQuery::eval(const TextQuery &text) const {
//通过Query运算对象进行的虚调用,以获得运算对象的查询结果set
auto left = lhs.eval(text), right = rhs.eval(text);
//保存left和right交集的set
auto ret_lines = make_shared<set<line_no>>();
//将两个范围的交集写入一个目的迭代器中
//本次调用的目的迭代器向ret添加元素
set_intersection(left.begin(), left.end(), right.begin(), right.end(),
inserter(*ret_lines, ret_lines->begin()));
return QueryResult(rep(),ret_lines, left.get_file());
}
class OrQuery : public BinaryQuery{
friend Query operator|(const Query &, const Query &);
OrQuery(const Query &left, const Query &right) : BinaryQuery(left, right, "|") {
cout << "OrQuery::OrQuery(const Query &, const Query &)" << endl;
};
//具体的类:AndQuery继承了rep并且定义了其它纯虚函数
QueryResult eval(const TextQuery &) const;
};
inline
Query operator|(const Query &lhs, const Query &rhs){
return shared_ptr<Query_base>(new OrQuery(lhs, rhs));
}
//返回运算对象查询结果set的并集
QueryResult
OrQuery::eval(const TextQuery &text) const {
//通过Query成员lhs和rhs进行的虚调用
//调用eval返回每个运算对象的QueryResult
auto right = rhs.eval(text), left = lhs.eval(text);
//将左侧运算对象的行号拷贝到结果set中
auto ret_lines = make_shared<set<line_no>>(left.begin(), right.begin());
//插入右侧运算对象所得的行号
ret_lines->insert(right.begin(), right.end());
//返回一个新的QueryResult,它表示lhs和rhs的并集
return QueryResult(rep(), ret_lines, left.get_file());
}
int main() {
cout << "Please type the file name: ";
string pt;
cin >> pt;
ifstream input(pt);
if (input.is_open()){
Query q = Query("fiery") & Query("bird") | Query("wind");
runQueries(input);
} else {
cerr << "Failed to open file" << endl;
return EXIT_FAILURE;
}
}