Skip to content

Latest commit

 

History

History
277 lines (251 loc) · 10.3 KB

File metadata and controls

277 lines (251 loc) · 10.3 KB
#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;
    }
}