c++文本查询程序再探(转)

include "stdafx.h"

include

include

include

include

include

include

include

include

include

include

using namespace std;

class QueryResult;//保存查询结果的类
class TextQuery//保存待查询文本,并对文本中各单词与其出现行号的集合的指针构建map
{
public:
    using line_no = vector::size_type;
    TextQuery(ifstream &);//从文件中读取文本构造TextQuery类
    QueryResult query(const string &) const;//返回对一个单词的查询结果,有点类似WordQuery
private:
    shared_ptr<vector> file;//动态分配内存保存文本内容,每行当做一个string放进vector(file)中
    map<string, shared_ptr<set<line_no>>> wm;//注意这里的map是文本中各单词与其(出现行号的集合)的指针之间的映射
};
TextQuery::TextQuery(ifstream &is):file(new vector)//并没有在初始化列表里一步到位进行初始化,具体的初始化细节是在函数体里实现的
{
    string text;
    while (getline(is, text))//读取文本中的每一行内容
    {
        file->push_back(text);//对file进行初始化
        int n = file->size() - 1;//记录当前行序号
        istringstream line(text);//将istringstream绑定在text上,便于对其中各单词进行操作,例子见P288
        string word;
        while (line >> word)//提取每行中的每个单词
        {
            //之所以加引用是因为lines是指向set的shared_ptr
            auto &lines = wm[word];//利用map的特性,单词第一次出现会自动创建一个关键字,省去了麻烦的判断
            if (!lines)//如果单词第一次出现lines将会是一个空指针,可我们并不想让它是个空指针,我们只是想让它指向一个空的set
                lines.reset(new set<line_no>);
            lines->insert(n);//将word当前所在的行号放进set里
        }
    }
}
class QueryResult
{
    friend ostream &print(ostream &, const QueryResult &);//非成员函数的类接口
public:
    using line_no = TextQuery::line_no;
    QueryResult() = default;//默认构函,虽然在实际使用中会出错,但还是显式定义了一个
    QueryResult(string s,shared_ptr<set<line_no>> p,shared_ptr<vector> f):sought(s),lines(p),file(f){}
    auto begin() const
    {
        return lines->begin();
    }
    auto end() const
    {
        return lines->end();
    }
    shared_ptr<vector> get_file() const
    {
        return file;
    }
private:
    string sought;//查询的内容(准确来说是一个式子)
    shared_ptr<set<line_no>> lines;//单词在文本中出现的行号
    shared_ptr<vector> file;//文本内容,用于在print中进行输出,一行一个string
};

QueryResult TextQuery::query(const string &sought) const
{
    static shared_ptr<set<line_no>> nodata(new set<line_no>);//假如找不到单词,就返回一个指向空内容内存的指针
    auto loc = wm.find(sought);//loc是指向pair的迭代器
    if (loc == wm.end())//没找到
        return QueryResult(sought, nodata, file);
    else//找到了
        return QueryResult(sought, loc->second, file);//这里不能用wm[sought],因为query是一个常量成员函数,而map的下表操作默认会改变map
}
ostream &print(ostream &os, const QueryResult &qr)//之所以用输出流作为参数和返回类型是便于不同的输出操作,比如可以标准输出也可以输出到文件里
{
    os << qr.sought << " occurs " << qr.lines->size() << " " << "time(s)" << endl;
    for (auto n : qr.lines)//n是line_no
    {
        os <<"(line_no:"<<n+1<<")"<< " " << (
qr.file)[n] << endl;
    }
    return os;
}
class Query_base//抽象基类
{
    friend class Query;
protected:
    using line_no = TextQuery::line_no;
    virtual ~Query_base() {}
private://将两个接口(纯虚)函数放在private里,是不想用户(派生类)直接使用Query_base,后面接口类将两个函数放在public里就行了,毕竟各种query实际都是Query类的对象
    virtual QueryResult eval(const TextQuery&) const = 0;//实际上求返回结果就是求了个QueryResult
    virtual string rep() const= 0;
};
class Query//接口类,用于隐藏Query_base的继承体系
{
    //这些运算符都用到了从指针创建Query对象的构造函数
    friend Query operator~(const Query &);
    friend Query operator|(const Query &, const Query &);
    friend Query operator&(const Query &, const Query &);
public:
    Query(const string&);//与其他类型的query不同,WordQuery可以直接由Query类构造得到
    QueryResult eval(const TextQuery &t) const
    {
        return q->eval(t);//为什么在Query_base里两个函数是private的,在这儿却能从外部调用?
        //因为Query是Query_base的友元类,用q调用eval/rep函数都是在静态类型中进行名字查找的,所以Query_base的
        //各派生类并不需要对Query类进行友元声明,它们只进行动态绑定
    }
    string rep() const { return q->rep(); }
private:
    Query(shared_ptr<Query_base> query) :q(query){}//用于各个运算符的定义,但又不想外部访问,所以各运算符声明为了友元函数
    shared_ptr<Query_base> q;//各种query实际存储在这里
};
ostream &operator<<(ostream &os, const Query &query)//不能定义在类Query里(定义在类内改变某类对象的该运算符操作,定义在类外则会改变所有对象的该运算符操作)
{
    return os << query.rep();//因为rep在基类里是虚函数,所以这里的调用会执行动态绑定
}
class WordQuery :public Query_base
{
    friend class Query;//Query类用到了WordQuery的构造函数,而友元关系不能继承,只能再次声明
    WordQuery(const string &s) :query_word(s){}
    QueryResult eval(const TextQuery &t) const
    {
        return t.query(query_word);//WordQuery的查询可以直接由TextQuery实现
    }
    string rep() const
    {
        return query_word;//有点怪怪的,根据之前的输出运算符,难道WordQuery就输出一个查询单词???
        //rep函数只是将输入的内容转换为标准的运算式形式,并不是用来输出查询结果的,应该是用QueryResult的print函数来输出查询结果
    }
    string query_word;
};
inline Query::Query(const string&s) :q(new WordQuery(s)){}//new返回的普通指针可以直接用来初始化智能指针
class NotQuery :public Query_base
{
    //不需要对Query类进行友元声明
    friend Query operator~(const Query&);//~运算符使用了NotQuery的构造函数
    NotQuery(const Query &q) :query(q){}//这个构造函数并无任何问题,在NotQuery里存着一个要取反的Query而已
    //对两个接口函数进行覆盖
    QueryResult eval(const TextQuery&) const;//其实真正区分各种Query的是eval函数
    string rep() const override
    {
        return "~(" + query.rep() + ")";
    }
    Query query;//要对哪个Query取反
};
inline Query operator~(const Query &q)
{
    return shared_ptr<Query_base>(new NotQuery(q));
    //就是在这种情况用到了从指针到Query对象的转换,需要Query的构造函数
    //还使用了NotQuery类的构造函数
}
class BinaryQuery :public Query_base//抽象基类
{
protected://下面这些成员都要在派生类中使用
    BinaryQuery(const Query &q1,const Query &q2,string os):lhs(q1),rhs(q2),opSym(os){}
    //不定义eval函数,直接继承一个纯虚函数,反正自己也是一个抽象基类
    string rep() const override
    {
        return "("+lhs.rep() + " "+opSym+" " + rhs.rep()+")";
    }
    Query lhs;
    Query rhs;
    string opSym;//运算符
};
class AndQuery :public BinaryQuery
{
    friend Query operator&(const Query &, const Query &);
    AndQuery(const Query &q1,const Query &q2):BinaryQuery(q1,q2,"&"){}
    QueryResult eval(const TextQuery &) const;
};
inline Query operator&(const Query &q1,const Query &q2)
{
    return shared_ptr<Query_base>(new AndQuery(q1, q2));
}
class OrQuery :public BinaryQuery
{
    friend Query operator|(const Query &, const Query &);
    OrQuery(const Query &q1,const Query &q2):BinaryQuery(q1,q2,"|"){}
    QueryResult eval(const TextQuery &) const;
};
inline Query operator|(const Query &q1, const Query &q2)
{
    return shared_ptr<Query_base>(new OrQuery(q1, q2));
}
//eval实际就是求lines,lines是创建QueryResult的关键
QueryResult NotQuery::eval(const TextQuery &t) const
{
    //先求得单词出现的行号
    auto result = query.eval(t);
    auto ret_lines = make_shared<set<line_no>>();
    //对于输入文件中的每一行,如果没有出现在result中,则把它添加到ret_lines里
    auto beg = result.begin();
    auto end = result.end();
    auto sz = result.get_file()->size();
    for (size_t n = 0;n != sz;++n)//在文本中遍历
    {
        if (beg == end || n != beg)//result中的遍历已经结束或者当前行号不在result中,只有file中的指针后移一位
                                    //之所以能这么写,是因为result和file中的行号都是按顺序排列的
        {
            ret_lines->insert(n);
        }
        else if (beg != end)//当前行号在result当中且result还没遍历完,result和file中的指针都向后移动一位
        {
            ++beg;
        }
    }
    return QueryResult(rep(), ret_lines, result.get_file());
}
QueryResult OrQuery::eval(const TextQuery &t) const
{
    //得到两个Query对象的QueryResult
    auto right = rhs.eval(t);
    auto left  = lhs.eval(t);
    //创建行号列表的指针
    auto ret_lines = make_shared<set<line_no>>(left.begin(), left.end());
    ret_lines->insert(right.begin(), right.end());//set会自动忽略重复的关键字
    return QueryResult(rep(), ret_lines, left.get_file());
}
QueryResult AndQuery::eval(const TextQuery &t) const
{
    auto left = lhs.eval(t);
    auto right = rhs.eval(t);
    auto ret_lines = make_shared<set<line_no>>();
    //这个函数将两个查询结果的交集添加到ret_lines中
    set_intersection(left.begin(), left.end(), right.begin(), right.end(), inserter(
ret_lines, ret_lines->begin()));//原来是在这里出了问题
    return QueryResult(rep(), ret_lines, left.get_file());
}
int main()
{
    ifstream ifs("d://TextQuery.txt");
    TextQuery tq(ifs);//TextQuery会自动创建好file和map
    /Query q("Alice");/
    //事实上直接使用Query的构造函数的都是WordQuery,其它的Query其实都是由WordQuery通过运算符运算得到的
    Query q = Query("Alice") | Query("wind");
    QueryResult qr=q.eval(tq);
    print(cout, qr);
    return 0;
}

原文链接: https://www.cnblogs.com/yuanhub/p/13150645.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;

也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬

    c++文本查询程序再探(转)

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/356328

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年3月2日 上午11:09
下一篇 2023年3月2日 上午11:10

相关推荐