C++学习之第十七天-再探文本查询-实现与、或、非查询方法

        代码的实现基本是手敲一遍C++ Primer5 15.9文本查询再探,进一步巩固,继承、抽象基类、虚函数等等知识点。

面向对象程序设计

基于Day14天的文本查询进行扩展:

        TextQuery类:该类中有个query查询操作,返回一个结果存在QueryResult中

        QueryResult类:保存一个单词的查询结果:单词、行号集合、句子集合

1.Query_base_抽象层,提供抽象接口。

        虚函数eval:接受一个TextQuery,返回结果存储在QueryResult对象中,eval函数主要在业务层Query_base中实现,eval函数在不同的派生类中重写,返回不一样的结果。

2.Query类--实现层,提供用户外部接口,用于隐藏整个继承体系。

        1.Query类提供一个指向Query_base智能指针p,用该指针绑定到Query_base的派生类身上,用户通过对Query对象的操作,间接地创建并且处理Query_base对象。

        2.Query有一个接受一个string参数的构造函数,创建一个新的WordQuery对象,然后将指向Query_base的智能指针绑定到派生类WordQuery身上。

        3.对于NotQuery,AndQuery,OrQuery这三个派生类,Query提供一个接受指向Query_base的智能指针shared_ptr的构造函数,用来初始化指向Query_base的智能指针p,这个构造函数是为了支持&、|、~运算符而设定。

扫描二维码关注公众号,回复: 13289774 查看本文章

        4.Query类中重写Query_base中的eval(),rep(),间接调用Query_base的派生类对象的eval()和rep()函数。

3.WordQuery类:Query_base的派生类,用于查找一个给定的单词

4.NotQuery类:Query_base的派生类,查询的结果是Query运算对象没有出现的句子集合

5.BinaryQuery类:Query_base派生出来的另外一个抽象基类,表示双目运算查询

6.AndQuery:BinaryQuery的派生类,两个单词同时出现的行的交集

7.OrQuery:BinaryQuery的派生类,两个单词出现的行的并集

完整代码:分为 TextQuery. h、Query_base.h和main.test文件

TextQuery.h

该代码基本已在Day14的文本查询中完成,这里增加了:

1.防止头文件包含宏指令。---不能少

2.QueryResult中多了三个函数:

       1. set<line_no>::iterator begin()---返回行号集合的首尾置迭代器

        2.set<line_no>::iterator end()---返回行号集合的末尾迭代器

        3.shared_ptr<vector<string>> &get_file()//获取句子容器vector的智能指针        

#ifndef __TEXTQUERY_H__
#define __TEXTQUERY_H__ //防止头文件重复包含,必须写
#include <iostream>
#include<fstream>
#include <sstream>
#include <iterator>
#include <stdio.h>
#include <vector>
#include <map>
#include <set>
#include <memory>
#include <string>
using std::istringstream;
using std::set;
using std::pair;
using std::ifstream;
using std::ostream;
using std::string;
using std::vector;
using std::cout;
using std::endl;
using std::cerr;
using std::map;
using std::shared_ptr;
using std::cin;
class QueryResult;
class TextQuery
{
public:
    using line_no = vector<string>::size_type;//给vector<string>::size_type起别名
    //size_type:是个unsigned类型,其长度与机器匹配,ubuntu下,为unsigned long类型
    TextQuery(ifstream &);//构造函数,传入文件名
    QueryResult query(const string &)const; //查询,
private:
    shared_ptr<vector<string>> file;//输入文件
    map<string, shared_ptr<set<line_no>>> wm;//一个智能指针管理一个set
};
class QueryResult
{
	friend ostream& print(ostream &,const QueryResult &);//打印输出
public:
    using line_no = vector<string>::size_type;//给vector<string>::size_type起别名
    set<line_no>::iterator begin()
    {
        return lines->begin();//返回第一个行号的迭代器
    }

    set<line_no>::iterator end()
    {
        return lines->end();//最后一个行号的迭代器
    }

    shared_ptr<vector<string>> &get_file()
    {
        return file;//单词所对应句子的智能指针
    }
    
    QueryResult(string s, shared_ptr<set<line_no>> p,shared_ptr<vector<string>> f)
    :sought(s),lines(p),file(f) //有参构造函数,
    {
    }  

private:
    string sought;//用于查询单词
    shared_ptr<set<line_no>> lines;//保存,出现的行号
    shared_ptr<vector<string>> file;//输入文件
};
void parseText(string &text)//解析句子,大写换小写,去掉句子首尾空格
{
    for(int i=0;i<text.size();i++)
    {
        if(text[i]>='A'&&text[i]<='Z')
        {
            text[i] = tolower(text[i]);
            //text[i]=toupper(text[i]);
            continue;
        }
        if(text[i]>='a'&&text[i]<='z')
        {
            continue;
        }

        text[i]=' ';
        
    }
    //去掉句子首尾空格
    if(!text.empty())
    {
        text.erase(0,text.find_first_not_of(" "));
        text.erase(text.find_last_not_of(" ")+1);
    }
}
TextQuery::TextQuery(ifstream &is)
:file(new vector<string>)//file是shared_ptr智能指针,用来管理vector<string>容器
{
    string text;
    while(getline(is,text))//获取文件中的每一行
    {
        file->push_back(text);//把每一行尾插入容器
        int n = file->size()-1; //当前的行号
        parseText(text);        //把每一行的标点符号换成空格,去掉首尾的空格,大写变小写
        istringstream line(text);//把文本分解为单词
        string word;
        while(line>>word)
        {
            //如果单词不在wm中,以之为下标wm中添加一项
            auto &lines = wm[word];//lines容器中存储的是shared_ptr智能指针,每个智能指针管理一个set
            if(!lines)//第一次遇到这个单词的时候,需要创建管理set的智能指针
                //如果单词对应的智能指针不存在的话,lines会为空
                lines.reset(new set<line_no>);//碰到新单词,给此单词创建一个智能指针,用来管理此单词的行号set
            lines->insert(n);//插入行号
        }
    }
}

QueryResult TextQuery::query(const string &sought)const //查询
{//sought是要查找的单词
    static shared_ptr<set<line_no>> nodata(new set<line_no>);
    auto loc = wm.find(sought);//wm是个map(string, shared_ptr<set<unsigned long>>)容器
    //wm.find(sought)的结果是sought单词的迭代器
    if(loc==wm.end())
        return QueryResult(sought,nodata,file);//未找到
    else
        return QueryResult(sought,loc->second,file);
    
}
string make_plural(size_t ctr,const string &word,const string &ending)
{
    return (ctr>1)?word+ending:word;
}
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)
        os<<"\t(line "<<num+1<<") "
            <<*(qr.file->begin()+num)<<endl;

    return os;
}
#endif

Query_base.h

1.set_intersection()函数(取两个set的交集)和set_union函数(并集)在algorith头文件下,把#include<algorithm>放到TextQuery.h中时,会报错?

2.写完WordQuery和NotQuery,测试一遍,理解一遍,再去读OrQuery和AndQuery代码,勉强能理解了,还得多看。

#ifndef __QUERY_BASE_H__
#define __QUERY_BASE_H__
#include "TextQuery.h"
#include <algorithm>
class Query_base  //抽象层
{
    friend class Query;

protected:
    using line_no = TextQuery::line_no;
    virtual ~Query_base() = default;

private:
    // eval返回与当前Query匹配的QueryResult
    virtual QueryResult eval(const TextQuery&) const = 0;
    // rep 是表示查询的一个string
    virtual std::string rep() const = 0;
};


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 std::string&);  //1.构建一个新的WordQuery
    //接口函数:用于调用对应的Query_base操作
    QueryResult eval(const TextQuery &t) const
    {
        return q->eval(t); //eval接口函数,先去调用Query_base派生类的eval函数
        //最终是要去调用TextQuery类中的query函数,返回的结果保存在QueryResult类的对象中
    }

    std::string rep() const//rep接口函数:
    {
        return q->rep();
    }

private:
    Query(std::shared_ptr<Query_base> query)
    : q(query)//用于~、&、|类的初始化,让Query_base的指针指向其派生类,均在友元函数中完成对q的初始化
    {
    }

    std::shared_ptr<Query_base> q;//智能指针q,指向Query_base接口类,用来实现多态
};

//业务层:查询单词
class WordQuery
: public Query_base
{
    friend class Query;  // Query使用WordQuery构造函数

    WordQuery(const std::string &s) //构造函数,
    : query_word(s)//query_word是要查询的单词,
    {

    }

    // 具体的类:WordQuery将定义所有继承而来的虚函数
    QueryResult eval(const TextQuery &t) const//t是TextQuery对象
    {
        return t.query(query_word);//调用TextQuery类中的query函数,得到的查询结果存在QueryResult
    }

    std::string rep() const//rep()是要进行查询的单词
    {
        return query_word;
    }

    std::string query_word;  //要查找的单词
};
inline
Query::Query(const std::string &s)//实现层Query的构造函数。
: q(new WordQuery(s)) //Query中的智能指针q由WordQuery new出来的
{}
/*
 //q在Query中是shared_ptr<Query_base>,指向抽象层Query_base的智能指针 q = new WordQuery(s)
//相当于用基类的指针 来接收派生类对象的指针。用此指针去调用基类的虚函数时,会去调用派生类的,实现多态
//WordQuery也是Query_base的派生类,此时用q->rep()或者q->eval()都会去调用派生类中重写的函数
//同时Query也是Query_base的派生类,用Query实例化出来的对象去调用Query内部的函数eval(),rep(),属于静态联编,但是Query中的eval(),rep()的函数体:q->eval(t), q->rep()实现多态,具体是看q是从哪个业务层的构造函数得来的。
如业务层,WordQuery类:
Query q1("with"); //这个实例化出来的对象,去调用Query(const std::string &s)构造函数,Query中的智能指针q由WordQuery new出来的,用q1.rep()和q1.eval(),会去调用WordQuery中重写的函数,实现多态
*/
//业务层~
class NotQuery
: public Query_base
{
    friend Query operator~(const Query &);
    
    NotQuery(const Query &q)//NotQury构造函数
    : query(q)//query是Query实例化出来的对象
    {
    }

    std::string rep() const//打印
    {
        return "~(" + query.rep() + ")";
    }

    QueryResult eval(const TextQuery&) const;//查询结果
    Query query; //Query 对象,用来接收~号后面的的Query对象
};
inline Query operator ~(const Query &operand)//~Query("with)
{
    //return std::shared_ptr<Query_base>(new NotQuery(operand));
    shared_ptr<Query_base> temp(new NotQuery(operand));//operand是Query对象,
    //temp是Query_base指针,NotQuery是Query_base的派生类,用temp指向new NotQuery(operand)
    return Query(temp);//会去调用Query(std::shared_ptr<Query_base> query)构造函数
    				//以temp的值来初始化Query中的p;
}
ostream & operator<<(ostream &os, const Query &rhs)
{
    os<<rhs.rep();
    return os;
}
//业务层取反:~,eval查询并且返回结果
QueryResult NotQuery::eval(const TextQuery& text) const//以~Query("with")为例子
{
    // 通过Query运算对象对eval进行虚调用
    QueryResult result = query.eval(text);//以~Query("with"),query就是:Query query("with");
    //result:with单词对应的行号、文本级所有句子都存储在result中
    // 开始时结果创建一个新set,初始时为空,用来存储不存在“with”单词的行号
    auto ret_lines = std::make_shared<set<line_no>>();
    // 我们必须在运算对象出现的所有行中进行迭代
    auto beg = result.begin();//在QueryResult类中,返回的是“with”单词行号集合的第一个迭代器
    auto end = result.end();//行号集合的最后一个迭代器
    // 对于输入文件的每一行,如果该行不在
    auto sz = result.get_file()->size();
    //get_file()在QueryResult类中,返回的是管理句子vector的智能指针
    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());//把~Query("with")的结果返回,
    //rep()就是~("with")
}
//双目运算符&/|抽象基类,提供接口,和构造函数
class BinaryQuery:public Query_base
{
protected:
    BinaryQuery(const Query &l, const Query &r,std::string s)
    :lhs(l),rhs(r),opSym(s)//基类构造函数
    {
    }
    std::string rep()const
    {
        return "("+ lhs.rep()+" "//左侧Query对象
            +opSym + " "+       //运算符
            rhs.rep()+")";//右侧Query对象
    }

    Query lhs,rhs;//Query对象,给派生类AndQuery和OrQuery继承
    std::string opSym;//运算符
};
//业务层:&
class AndQuery:public BinaryQuery
{
    friend Query operator&(const Query &,const Query &);
    AndQuery(const Query &left,const Query &right)
    :BinaryQuery(left,right,"&")//调用基类BinaryQuery构造函数完成初始化
    {
    }
	//重写Query_base中的eval函数,实现查询功能
    QueryResult eval(const TextQuery&)const;
};
inline Query operator&(const Query &lhs,const Query &rhs)
{
    shared_ptr<Query_base> temp(new AndQuery(lhs,rhs));//
    return Query(temp);//初始化Query中的p,Query_base的指针p接收派生类AndQuery指针
}
//业务层& eval,得到查询结果
QueryResult AndQuery::eval(const TextQuery& text) const
{
    //通过Query运算对象进行的虚调用,以获得运算对象的查询结果set
    QueryResult left_result = lhs.eval(text);
    QueryResult right_result = rhs.eval(text);
    //保存left和right的交集的set
    auto ret_lines = std::make_shared<set<line_no>>();
    //ret_lines是管理行号set的智能指针,*ret_lines是迭代器本身
    //set_intersetion:头文件algorithm,获取两个set的交集,
    set_intersection(left_result.begin(), left_result.end(),
                     right_result.begin(),right_result.end(),
                     inserter(*ret_lines,ret_lines->begin()));//inserter:插入迭代器
    return QueryResult(rep(), ret_lines, left_result.get_file());//返回结果
}
//业务层:|
class OrQuery:public BinaryQuery//或运算
{
    friend Query operator|(const Query&,const Query&);
    OrQuery(const Query &left,const Query &right)
    :BinaryQuery(left,right,"|")//在基类BinaryQuery中完成初始化
    {

    }
	//重写祖类Query_base中的eval,实现多态
    QueryResult eval(const TextQuery&)const;
};

inline Query operator|(const Query &lhs,const Query& rhs)
{
    shared_ptr<Query_base> temp(new OrQuery(lhs,rhs));//祖类的智能指针指向派生类OrQuery
    return Query(temp);//初始化Query中的祖类指针p
}
//业务层eval:实现或查询,返回查询结果
QueryResult OrQuery::eval(const TextQuery& text)const
{
    QueryResult left_result = lhs.eval(text);
    QueryResult right_result = rhs.eval(text);

    auto ret_lines = std::make_shared<set<line_no>>();
    //并集,set_union的用法跟set_intersection类似
    set_union(left_result.begin(),left_result.end(),
              right_result.begin(),right_result.end(),
              inserter(*ret_lines, ret_lines->begin()));

    return QueryResult(rep(),ret_lines, left_result.get_file());
}




#endif

main_test.cc

#include "TextQuery.h"
#include "Query_base.h"

void test01()
{
    ifstream is("china_daily.txt");
    if(!is.good())
    {
        cout<<"ifstream is not good"<<endl;
        return;
    }
    TextQuery t1(is);
   // QueryResult q = t1.query("with");
   // print(cout, q);
   cout<<"测试1:查询单词with以及打印其所在句子:"<<endl;
   Query q("with");
   print(cout,q.eval(t1));//打印结果
   //cout<<endl<<endl;
   
 // print(cout,(Query("with")&Query("like")).eval(t1));  
  //cout<<endl<<endl;

  //print(cout, (Query("with")|Query("your")).eval(t1));
}
void test02()
{
    ifstream is("china_daily.txt");
    if(!is.good())
    {
        cout<<"ifstream is not good"<<endl;
        return;
    }
    TextQuery t1(is);

   cout<<"测试2:查询~"<<Query("the").rep() <<",打印不存在"<<Query("the").rep()<<"单词的句子:"<<endl;
   cout<<~Query("the")<<endl;//(~p).rep();//重载<<运算符,直接打印对象
   cout<<(~Query("the")).rep()<<endl;//(~with)
   print(cout, (~Query("the")).eval(t1));
}
void test03()
{
    
    ifstream is("china_daily.txt");
    if(!is.good())
    {
        cout<<"ifstream is not good"<<endl;
        return;
    }
    TextQuery t1(is);
    cout<<"测试3:与&测试"<<endl;
    Query q1("with");
    Query q2("like");

    print(cout, (q1&q2).eval(t1));
    cout<<endl<<endl;
    cout<<"测试4:或|测试"<<endl;
    print(cout,(q1|q2|Query("your")).eval(t1));

}
int main()
{
    //test01();
    test02();
    //test03();
    return 0;
}

运行结果:

test01():

测试1:查询单词with以及打印其所在句子:
with occurs 13 times
    (line 15) private firms with high growth potential, Gao said.
    (line 24) Bloomberg reported earlier citing people familiar with the matter that the launch 
    (line 42) Chinese travelers like to take instant noodles with them while travelling abroad, 
    (line 44) And the favorite items travelers like to bring with them in their luggage differed 
    (line 60) Quanjude, China's iconic restaurant chain for original Peking roast duck with a 
    (line 61) history since 1864, has embraced the nation's "Internet Plus" strategy, with a new 
    (line 63) "Internet Plus" has sparked integration of the Internet with traditional industries, 
    (line 71) with Quanjude and chairman of a new joint-venture company that is pursuing the online takeout and e-commerce market.
    (line 74) the company, Yage Technology Inc, in October 2015 with Chongqing Kuangcao Technology 
    (line 77) "We believe with our time-honored brand image, experienced artisan cooking skills, 
    (line 87) duck rolls are made in Quanjude restaurants, with the same recipe and ingredients 
    (line 94) according to Yang Aixiang, general manager with Yage Technology.
    (line 104) Yang Xun, a publicist with Baidu Takeout, which handles delivery service of

test02():~测试

测试2:查询~the,打印不存在the单词的句子:
~(the)
~(the)
~(the) occurs 69 times
    (line 1) 1.Shenzhen-HK stock link 'likely in second quarter': UBS
    (line 2) 
    (line 6) investors, a senior analyst at UBS Securities said on Wednesday.
    (line 7) 
    (line 11) Stock Connect, said Gao Ting, chief China equities strategist at UBS Securities.
    (line 12) 
    (line 14) market, unlike Shanghai, is home to many high-tech, innovative companies and 
     .....

test03():&和|测试

测试3:与&测试
(with & like) occurs 2 times
    (line 42) Chinese travelers like to take instant noodles with them while travelling abroad, 
    (line 44) And the favorite items travelers like to bring with them in their luggage differed 


测试4:或|测试
((with | like) | your) occurs 14 times
    (line 15) private firms with high growth potential, Gao said.
    (line 24) Bloomberg reported earlier citing people familiar with the matter that the launch 
    (line 40) 2.Going abroad? Don't forget your instant noodles
    (line 42) Chinese travelers like to take instant noodles with them while travelling abroad, 
    (line 44) And the favorite items travelers like to bring with them in their luggage differed 
    (line 60) Quanjude, China's iconic restaurant chain for original Peking roast duck with a 
    (line 61) history since 1864, has embraced the nation's "Internet Plus" strategy, with a new 
    (line 63) "Internet Plus" has sparked integration of the Internet with traditional industries, 
    (line 71) with Quanjude and chairman of a new joint-venture company that is pursuing the online takeout and e-commerce market.
    (line 74) the company, Yage Technology Inc, in October 2015 with Chongqing Kuangcao Technology 
    (line 77) "We believe with our time-honored brand image, experienced artisan cooking skills, 
    (line 87) duck rolls are made in Quanjude restaurants, with the same recipe and ingredients 
    (line 94) according to Yang Aixiang, general manager with Yage Technology.
    (line 104) Yang Xun, a publicist with Baidu Takeout, which handles delivery service of

猜你喜欢

转载自blog.csdn.net/weixin_49278191/article/details/121389387
今日推荐