C++详解Primer文本查询程序的实现

 更新时间:2022年06月24日 09:41:02   作者:配的上了吗  
这个程序还是比较复杂的,把这句话作为文章的开头可以看出它的真实性.....这篇文章主要介绍了文本查询程序的实现,下面我们一起来看看

15.9的文本查询程序是对12.3节的文本查询程序的扩展,而使用的主要知识也是15章的核心:继承和多态,即面向对象程序设计。

恩,这一节看的过程中,会有很多不理解。特别是在没有把整个程序都看完之前,会有很多疑惑,而看完之后,再思考思考,回头再看本节的前面所写的程序介绍,会有一些感悟。更加清楚这个程序的原理。

TextQuery.h

#ifndef QUERY_TEXTQUERY_H
#define QUERY_TEXTQUERY_H
#include<iostream>
#include<vector>
#include<string>
#include<memory>
#include<map>
#include<set>
#include<cstdio>
#include<cstdlib>
#include<sstream>
#include<algorithm>
#include<fstream>
#include<stack>
using namespace std;
class QueryResult;
// ??????????????????????
class TextQuery
{
public:
    using line_no = vector<string>::size_type;
    TextQuery(ifstream&); // ?????????????????????????????в????????
    QueryResult query(const string&) const;  // ???????string???????string???в????
private:
    shared_ptr<vector<string>> file;  // ???????
    map<string, shared_ptr<set<line_no>>> wm;  // ???????????????????????к?set??
};
inline TextQuery::TextQuery(ifstream &is): file(new vector<string>) {
    string text;
    while (getline(is, text)) {   // ??????е?????
        file->push_back(text);    // ????????е????
        int n = file->size() - 1;  // ???浱????к???????·?????????????shared_ptr<set<line_no>> ?????????к?
        istringstream line(text);
        string word;
        while(line >> word){
            auto &lines = wm[word];  // lines?????shared_ptr
            if(!lines)   // ??????±??????????map?д????????word?????word????????shared_ptr??????shared_ptr???????nullptr;
                lines.reset(new set<line_no>);
            lines->insert(n);
        }
    }
}
class QueryResult{
    friend ostream& print(ostream& os, const QueryResult& qr);
public:
    QueryResult(string s, shared_ptr<set<TextQuery::line_no>> p, shared_ptr<vector<string>> f)
            : sought(s), lines(p), file(f) {}
    auto begin()const {return lines->begin();}
    auto end()const{return lines->end();}
    shared_ptr<vector<string>> get_file()const {return file;}
private:
    string sought; // ????????
    shared_ptr<set<TextQuery::line_no>> lines;  // ??????к?
    shared_ptr<vector<string>> file;  // ???????
};
inline QueryResult TextQuery::query(const string& s)const {
    static shared_ptr<set<line_no>> nodata(new set<line_no>()); // ?????????????к?set??shared_ptr
    auto loc = wm.find(s);
    if(loc == wm.cend())
        return QueryResult(s, nodata, file);
    else
        return QueryResult(s, loc->second, file);
}
inline ostream& print(ostream& os, const QueryResult& qr)
{
    os<<qr.sought<<" occurs "<<qr.lines->size()<<" "<<((qr.lines->size()>1)?"times":"time")<<endl;
    for(const auto& num: *(qr.lines)){
        os << "\t(lines "<<num+1<<") "<<(*(qr.file))[num]<<endl;
    }
    return os;
}
inline void runQueries(ifstream& infile)
{
    // infile?????ifstream??????????????????
    TextQuery  tq(infile);
    // ????????????????????????????????????????????
    while(true)
    {
        cout<<"enter word to look for, or q to quit: "<<endl;
        string s;
        // ?????????????s=='q'?????
        if(!(cin>>s) || s == "q")  break;
        print(cout, tq.query(s)) << endl;
    }
}
inline void independent_word_query(ifstream& ifile){
    vector<string> file;
    map<string,set<int>> word_map;
    string text;
    while(getline(ifile, text)){
        file.push_back(text);
        int n = (int)file.size()-1;  // ???push_back?????е??±?
        istringstream line(text);   // ???string text???????????
        string word;
        while(line >> word){
            word_map[word].insert(n);
        }
    }
    while(true){
        cout << "Enter word to look for, or q to quit: "<<endl;
        string s;
        if(!(cin>>s) || s=="q")  break;
        const auto& lines = word_map.find(s); // ?????????????????????pair<string,set<int>>
        cout<<s<<" occurs "<<(*lines).second.size()<<(lines->second.size()>1?" times":" time")<<endl;
        for(const auto& i:lines->second){
            cout<<"\t(lines "<<i+1<<") "<<file[i]<<endl;
        }
    }
}
#endif

Query.h

#ifndef QUERY_QUERY_H
#define QUERY_QUERY_H
#include"TextQuery.h"
class Query_base
{
    friend class Query;
protected:
    using line_no = TextQuery::line_no;   // �����������eval������ʹ�ã�������Ϊprotected��
    virtual ~Query_base() = default;
private:
    // eval���������뵱ǰQueryƥ���QueryResult
    virtual QueryResult eval(const TextQuery&) const = 0;
    // rep��ʾ��ѯ��һ��string
    virtual string rep() const = 0;
};
class Query
{
    friend Query operator|(const Query&, const Query&);
    friend Query operator&(const Query&, const Query&);
    friend Query operator~(const Query&);
public:
    Query(const string&);    // ����һ���µ�WordQuery
public:
    QueryResult eval(const TextQuery& t)const { return q->eval(t); }
    string rep()const { return q->rep(); }
private:
    Query(shared_ptr<Query_base> query) :q(query){}
    shared_ptr<Query_base> q;
};
//ostream& operator<<(ostream& os, const Query& q)
//{
//    // Query::repͨ������Query_baseָ���rep�����������
//    return os<<q.rep();
//}
class WordQuery: public Query_base
{
    friend Query;
private:
    WordQuery(const string& s): query_word(s) {}
    virtual QueryResult eval(const TextQuery& t)const override
            { return t.query(query_word); }
    virtual string rep() const override
            { return query_word; }
    string query_word;
};
inline Query::Query(const string& s): q(new WordQuery(s)) {}  // ��Query���ָ���Աָ���·���Ķ���
// Query q = ~Query("dog");
class NotQuery: public Query_base
{
    friend Query operator~(const Query&);
private:
    NotQuery(const Query& q): query(q) {}
    virtual QueryResult eval(const TextQuery&) const override;
    virtual string rep() const override
    { return "~("+query.rep()+")"; }
    Query query;
};
inline Query operator~(const Query &operand)
{
    return Query(shared_ptr<Query_base>(new NotQuery(operand)));
}
class BinaryQuery: public Query_base
{
protected:
    BinaryQuery(const Query& l, const Query &r, string s):
    lhs(l), rhs(r), opSym(s) {}
    // ������࣬BinaryQuery������eval;
    virtual string rep()const override { return "(" + lhs.rep() + " " + opSym + + " " + rhs.rep() + ")"; }  // �������rep����������
    Query lhs, rhs;
    string opSym;
};
class AndQuery: public BinaryQuery
{
    friend Query operator&(const Query&, const Query&);
private:
    AndQuery(const Query& left, const Query& right): BinaryQuery(left,right,"&") {}
    // �̳���rep��������eval
    QueryResult eval(const TextQuery&) const override;
};
inline Query operator&(const Query &lhs, const Query &rhs)
{
    return Query (shared_ptr<Query_base>(new AndQuery(lhs,rhs)));
}
class OrQuery: public BinaryQuery
{
    friend Query operator|(const Query&, const Query&);
private:
    OrQuery(const Query& left, const Query &right): BinaryQuery(left,right,"|") {}
    QueryResult eval(const TextQuery&)const override;
};
inline Query operator|(const Query &lhs, const Query &rhs)
{
    return Query(shared_ptr<Query_base>(new OrQuery(lhs,rhs)));
}
#endif //QUERY_QUERY_H

Query.cpp

#include "Query.h"
// Query q = Query("dog") | Query("cat");
QueryResult OrQuery::eval(const TextQuery& t) const
{
    auto left = lhs.eval(t), right = rhs.eval(t);
    shared_ptr<set<line_no>> ret_lines(new set<line_no>(left.begin(),left.end()));
    ret_lines->insert(right.begin(),right.end());
    return QueryResult(rep(),ret_lines,lhs.eval(t).get_file());
}
QueryResult AndQuery::eval(const TextQuery& text) const
{
    auto left = lhs.eval(text), right = rhs.eval(text);
    auto ret_lines = make_shared<set<line_no>>();  // ����set���Ĭ�Ϲ��캯������Ĭ�ϳ�ʼ��
    set_intersection(left.begin(),left.end(),right.begin(),right.end(),inserter(*ret_lines,ret_lines->begin()));
    return QueryResult(rep(),ret_lines,left.get_file());
}
QueryResult NotQuery::eval(const TextQuery& text) const
{
    auto result = query.eval(text);
    auto ret_lines = make_shared<set<line_no>>();
    auto beg = result.begin(), end = result.end();
    auto sz = result.get_file()->size();
    for(size_t n = 0; n != sz; ++n){
        if(beg == end || *beg != n)
            ret_lines->insert(n);
        else
            ++beg;
    }
    return QueryResult(rep(), ret_lines, result.get_file());
}

main.cpp

void read_file(ifstream &f){
    TextQuery textquery(f);
    Query q ( Query("dog") & Query("cat"));
    print(cout,q.eval(textquery))<<endl;
    Query q2 = Query("bbb");
    print(cout, q2.eval(textquery))<<endl;
    Query q3 = ~Query("dog");
    print(cout, q3.eval(textquery))<<endl;
    system("pause");
}
int main(){
    string filename;
    cout<<"Please enter filename"<<endl;
    cin >> filename;
    ifstream file(filename);
    if(!file){
        cout<<"Filename error"<<endl;
        return -1;
    }
    read_file(file);
    return 0;
}

出现了一些意外,代码中中文注释都是乱码。

针对程序所涉及的几个类的介绍和理解:

TextQuery类:

可以把每个TextQuery类对象看作一个文本文件,这个类将某个文本文件的内容保存在一个vector<string>中,并保存了每个单词对应的行号,而query函数就是接收一个string,然后查找这个单词。而这里的返回结果是一个QueryResult类对象。这个类只是用来保存一个查询结果,其实后续的& | ~的结果也都是这个QueryResult类对象

在12章时,我想过,为什么要设计这么一个类呢?如果直接在query函数中实现查找并打印不可以吗?其实这样是不太合适的,一个最直接的原因就是,在后方进行word1 & word2操作时,不方便,封装一个查询结果类更容易处理。这样,也可以支持更多的操作,而不仅仅是打印。

QueryResult类:

表示一个查询结果,通常与print函数联系起来使用,print用于打印这个查询结果。

后续的就是一些新的继承方面的类了,也就是为了支持word1 & word2 或者 word1 | word2 或者 ~word操作。而这些查询都建模成了相互独立的类,即AndQuery OrQuery NotQuery 而最基本的还有一个WordQuery,这些类都继承自一个抽象基类Query_base。

Query_base类:

最主要的就是两个成员函数:eval 和 rep,说真的,我觉得这两个名字起的并不好,当然受限于我的英文水平,其实eval就相当于TextQuery类的query函数,参数是TextQuery,即一个文本文件,然后在这个文本文件中执行查询操作,返回一个查询结果QueryResult。rep函数用于返回查询的string表示形式,比如~(word1 & word2)。

Query类:

这个类是很重要的,当然这句话是句废话.... 这个类的数据成员是一个基类的指针,而这也是这个程序支持面向对象编程和多态的根本原因。

这是一个接口类,它的成员函数仍然是eval和rep,调用的是基类指针所指向对象的eval和rep,基类指针或引用调用虚函数发生动态绑定。所以,Query类基类指针指向的对象,可能是继承体系中任何一种类型的对象。比如: Query q = Query("dog") & Query("cat"); 而这里的q的基类指针指向的就是一个AndQuery类的对象,调用的eval和rep也都是AndQuery类版本的eval和rep,而这个AndQuery类的数据成员就包括着右边&运算符左右两边的两个WordQuery类的对象,这里是使用了&运算符重载。operator& 返回的就是一个基类指针绑定到AndQuery类对象的Query类对象。返回值用于初始化q。这里调用的应该是Query类的拷贝构造函数吧

WordQuery类:

Query_base类的派生类,表示对于某个单词最直接的查询,覆盖了eval和rep,为什么说eval相当于query呢?这里的eval就是最明显的证明:这里的eval直接返回参数TextQuery类的query结果,就是对某个单词的查询结果。而后方的Not And Or,都没有调用这个query操作,他们操作的是Query类对象的查询结果。

NotQuery类:

这个类也是Query_base的派生类,表示~查询方式。~运算符重载之后,返回的就是一个绑定到NotQuery类对象上的Query类对象,而~作用的就是另一个Query类对象的eval查询结果。

AndQuery OrQuery类:

因为这两个类都操作两个Query类对象,所以又实现了一个BinaryQuery抽象基类,这个基类继承自Query_base,多了两个Query类对象的成员,以及一个操作符成员,用于表示& 还是 |。

这两个类所关联的是& |运算符,operator& 返回的分别是是基类指针绑定到AndQuery类对象上的Query类对象 。operator | 返回的是基类指针绑定到OrQuery类对象上的Query类对象,而这两个类的rep函数很简单,对于两个成员的rep函数进行一些简单加工即可,而eval函数,参数仍然是一个TextQuery类,在两个Query成员返回的QueryResult上进行处理,然后返回一个新的QueryResult对象。代表着一种& 或者 |操作之后的查询结果。

还有一个比较有趣的是:

如下代码:

Query q = Query("Dog") & Query("Cat") | Query("Bird");

Print( cout, q.eval(textquery) );

一共创建了三个WordQuery,一个AndQuery,一个OrQuery。先后顺序不太清楚。。。但是其实创建好q对象之后,这里面并没有什么查询结果,保存的只是这些单词,还有一些没有调用的成员函数eval和rep。

根据运算符优先级规则,q是一个基类指针指向OrQuery类对象的Query对象,而如果想打印出这个查询结果,必然是要调用eval函数的,参数表示,在这个文件里查找这三个单词。在OrQuery的eval调用的最开始,两个Query类对象数据成员的查询结果还没有出来,而在eval函数内部,计算了两个查询结果,一个是rhs数据成员的对Bird单词的查询,查询的位置就是那个textquery保存的文件内容。另一个是AndQuery的eval函数的返回结果,这个结果是对两个WordQuery类对象查询结果的&操作之后的结果。最后才对这两个QueryResult结果进行合并处理。然后返回一个新的查询结果。

现在看来,只有WordQuery类对象调用了TextQuery的query操作。而其余的Or Not And都是对其他的Query对象的查询结果进行加工。当然这些eval函数的参数都是同一个TextQuery。并且都是返回的QueryResult。

说真的,之前比较疑惑的是,我感觉这些eval函数的TextQuery参数的传递有些奇怪。对比之前12章的文本查询程序,最后封装的对文件的查询的函数,看上去就舒服多了,它是把ifstream类对象传递给TextQuery类的构造函数的参数,然后后面调用query函数进行查询,返回一个QueryResult对象。调用print函数打印。

但是这里再探的程序就有点不一样和奇怪了,你也可以封装一个完整的查询函数,但是如果不那样做的话,进行的操作就是。

Query q = Query("Dog") & Query("Cat") | Query("Bird");

Print( cout, q.eval(textquery) );

这种操作,相比于 TextQuery tq(ifile); print(cout, tq.query("Dog")); 就有点奇怪了。

上方的main.cpp主函数,并没有实现完善的查询函数,即实时查询操作,如输入Dog & Cat | Bird。然后打印查询结果,之后有能力实现的话可能会补上。

就以这篇文章作为大一学习生活的结束吧。

到此这篇关于C++详解Primer文本查询程序的实现的文章就介绍到这了,更多相关C++文本查询内容请搜索得牛网以前的文章或继续浏览下面的相关文章希望大家以后多多支持得牛网!

相关文章

  • OpenCV实现马赛克和毛玻璃滤镜特效

    OpenCV实现马赛克和毛玻璃滤镜特效

    这篇文章主要为大家详细介绍了OpenCV实现马赛克和毛玻璃滤镜特效,具有一定的参考价值,感兴趣的小伙伴们可以参考一下方法
    2019-05-05
  • C++语言const 关键字使用方法图文详解

    C++语言const 关键字使用方法图文详解

    在类中,如果你不希望某些数据被修改,可以使用const关键字加以限定。const 可以用来修饰成员变量、成员函数以及对象
    2020-01-01
  • C语言每日练习之进制转换

    C语言每日练习之进制转换

    这篇文章主要介绍了C语言进制转换,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-11-11
  • 详解c/c++赋值函数(重载=号运算符)

    详解c/c++赋值函数(重载=号运算符)

    大家都知道c++里的各种运算符都是用函数实现的,比如=就等号函数,所以当用=给一个对象赋值的时候,实际调用的是=号所对应的=号函数。下面通过本文给大家介绍c/c++赋值函数(重载=号运算符),感兴趣的朋友一起看看吧
    2018-08-08
  • C语言实现俄罗斯方块的六种模式详程建议收藏

    C语言实现俄罗斯方块的六种模式详程建议收藏

    迟早一定会挂掉的俄罗斯方块,为什么至今仍是世界游戏之王?它是怎么编写的?本文将给大家详细介绍六种模式的实现,对大家的学习或工作具有一定的参考借鉴价值
    2022-02-02
  • 如何用C写一个web服务器之GCC项目编译

    如何用C写一个web服务器之GCC项目编译

    本文主要介绍了,如何用C写一个web服务器,Linux下用GCC进行项目编译,对此感兴趣的同学,可以参考下。
    2021-05-05
  • C语言修炼之路数据类型悟正法 解析存储定风魔上篇

    C语言修炼之路数据类型悟正法 解析存储定风魔上篇

    使用编程语言进行编程时,需要用到各种变量来存储各种信息。变量保留的是它所存储的值的内存位置。这意味着,当您创建一个变量时,就会在内存中保留一些空间。您可能需要存储各种数据类型的信息,操作系统会根据变量的数据类型,来分配内存和决定在保留内存中存储什么
    2022-02-02
  • C语言字符串快速压缩算法代码

    C语言字符串快速压缩算法代码

    这篇文章主要介绍了C语言字符串快速压缩算法代码,将字符串中连续出席的重复字母进行压缩,其主要的压缩字段的格式为”字符重复的次数+字符”。有需要的小伙伴参考下吧。
    2015-03-03
  • C语言数据结构与算法之排序总结(一)

    C语言数据结构与算法之排序总结(一)

    这篇文章主要介绍了数据结构与算法中的插入类和交换类的各种排序,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2021-12-12
  • C语言中操作sqlserver数据库案例教程

    C语言中操作sqlserver数据库案例教程

    这篇文章主要介绍了C语言中操作sqlserver数据库案例教程,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-07-07

最新评论