我的编程空间,编程开发者的网络收藏夹
学习永远不晚

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

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

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

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++文本查询内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

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

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

微信小程序实现分页查询详解

本篇文章给大家分享的是有关微信小程序分页查询的实现,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。
2022-11-13

微信小程序多表联合查询的实现详解

小程序设计中,通常会根据业务来做多表的拆分,多表拆分一般是根据业务的特点进行拆分。比如我们在文章关注的业务中,会将文章和关注信息拆分成一对多的表关系。初学者可能对一对一、一对多、多对多的设计概念不是特别清楚
2022-11-13

SQL Server实现全文搜索查询详解

目录一、概述二、全文搜索查询三、将全文搜索查询与 LIKE 谓词进行比较四、全文搜索体系结构4.1、SQL Server 进程4.2、过滤器守护程序主机进程五、全文搜索处理5.1、全文索引过程5.2、全文查询流程六、全文索引体系结构6.1、
2023-04-03

SQL Server实现全文搜索查询详解

这篇文章介绍了SQL Server的全文搜索功能,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
2023-05-14

SpringDataJPA实现排序与分页查询超详细流程讲解

在介绍SpringDataJPA的时候,我们首先认识下Hibernate。Hibernate是数据访问解决技术的绝对霸主,使用O/R映射技术实现数据访问,O/R映射即将领域模型类和数据库的表进行映射,通过程序操作对象而实现表数据操作的能力,让数据访问操作无须关注数据库相关的技术
2022-11-13

C#实现网络小程序的步骤详解

经常要检测某些IP地址范围段的计算机是否在线。有很多的方法,比如进入到网关的交换机上去查询、使用现成的工具或者编写一个简单的DOS脚本等等,这些都比较容易实现。本文将用C#来实现,感兴趣的可以了解一下
2022-12-27

TinkerPop框架查询Gremlin图实现过程详解

目录前言肇始于 TinkerPop 文档Structure 接口结构与实现思路Graph 接口Element 接口Vertex 接口Edge 接口Property 接口VertexProperty 接口实现自己的 Structure更进一步
2022-11-25

一文详解C++的程序流程控制

这篇文章主要介绍了一文详解C++的程序流程控制,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的小伙伴可以参考一下
2022-11-13

Java中JDBC实现动态查询的实例详解

一 概述1.什么是动态查询?从多个查询条件中随机选择若干个组合成一个DQL语句进行查询,这一过程叫做动态查询。2.动态查询的难点可供选择的查询条件多,组合情况多,难以一一列举。3.最终查询语句的构成一旦用户向查询条件中输入数据,该查询条件就
2023-05-31

C语言实现顺序表的基本操作的示例详解

顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。本文将通过示例为大家讲解一下顺序表的基本操作,需要的可以参考一下
2022-11-13

C#WinForm实现自动更新程序的方法详解

这一篇就着重写一下客户端的代码,客户端主要实现的有:启动后检测本地的xml文件,然后发送到服务器获取需要更新的文件以及版本列表,感兴趣的小伙伴可以了解一下
2022-11-13

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录