C++primer( 第五版 )第15章的Query文本查询功能的实现
实现Query的文本查询功能
第二次读primer时突然想把这个例程敲上去试一下效果,结果真是有不少坑需要注意,把这些错误纠正也是十分考验对C++11的掌握程度的。我是在Linux下用g++进行调试的,其实和在win10上用Visual studio无异。
问题产生的原因
出现问题的一个很大的一个原因就是书上应该是假设15.9中新涉及到的类都是在一个.cpp和.h文件下的,所以如果把每个类都分开,为每个类都新建一对.cpp/.h文件的话,各个文件之间的依赖关系非常复杂。但是由于我习惯把每个类分开定义,这样在即使程序很复杂的情况下,也能做到逻辑清晰,所以增加了不少麻烦。
QueryResult类的实现
QueryResult.h文件
#ifndef QUERYRESULT_H
#define QUERYRESULT_H
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <memory>
#include <set>
#include <algorithm> //这个库文件一定要包括,后边有一个函数定义在其中
using namespace std;
using line_no=vector<string>::size_type;
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){}
auto begin(){return (*lines).begin();}//这三个函数在书上的给定程序中好像并没有
auto end(){return lines->end();}//给出,需要自己定义,后边会需要
auto get_file(){return file;}//
private:
string sought;//需要查找的关键词
shared_ptr<set<line_no>> lines;///保存关键词出现的行号,行号的数量即是关键词出现的次数
shared_ptr<vector<string>> file;//读入的文件
};//类定义的结尾一定要加分号,易错点
ostream& print(ostream&,const QueryResult & );//输出函数只允许在这里进行声明
//如果不声明而是定义的话,会导致重定义,之后涉及到的非成员函数也是这样
#endif
QueryResult.cpp文件
#include "QueryResult.h"
ostream &print(ostream & os,const QueryResult &qr)
{
os<<qr.sought<<" occors "<<qr.lines->size()<<" "<<" times "<<endl;//输出关键词和出现次数
for (auto num : *qr.lines)
{
os<<" \t(line "<<num+1<<")"<<*(qr.file->begin()+num)<<endl;//输出关键词所在行
}
return os;
}
TextQuery类的实现
TextQuery.h文件是之后几种查询方式的基础
#ifndef TEXTQUERY_H
#define TEXTQUERY_H
#include "QueryResult.h"
#include <map>
class TextQuery
{
public:
TextQuery(ifstream&);
QueryResult query(const string&) const;
private:
shared_ptr<vector<string>> file;
map<string,shared_ptr<set<line_no>>> wm;
};
#endif
TextQuery.cpp
#include "TextQuery.h"
TextQuery::TextQuery(ifstream &is): file(new vector<string>)
{
string text;
while(getline(is,text))
{
file->push_back(text);//file分行保存读入的文本
int n=file->size() -1;
istringstream line(text);
string word;
while(line>>word)
{
auto &lines=wm[word];//wm是一个map,wm.second是一个set,保存每个单词出现的行,不能重复
if(!lines)
{
lines.reset(new set<line_no>);
}
lines->insert(n);
}
}
}
//这个函数是后边各种查询功能的基础
QueryResult TextQuery::query(const string &sought) const
{
static shared_ptr<set<line_no>> nodata(new set<line_no>);
auto loc=wm.find(sought);
if(loc == wm.end())
return QueryResult(sought,nodata,file);
else
return QueryResult(sought,loc->second,file);
}
各种查询类之间的继承关系
下图是各个类之间的继承关系:
Query_base类的实现
Query_base.h文件,是后边四种查询方式的基类
#ifndef QUERY_BASE_H
#define QUERY_BASE_H
//#include "Query.h"
#include "TextQuery.h"
class Query;//这里是只声明就可以,一定不能引用头文件,否则会造成两个类互相调用的情况,后边也有相似的情况
class Query_base //这也是分文件定义类引入的麻烦之一
{
friend class Query;
protected:
virtual ~Query_base()=default;
private:
virtual QueryResult eval(const TextQuery&) const =0;//这两个都是纯虚函数
virtual string rep() const =0;
};
#endif
Query_base.cpp文件//没有实质性内容,只是为了匹配Linux下编译的需求
#include "Query_base.h"
Query类的实现
Query.h文件
#ifndef QUERY_H
#define QUERY_H
#include "TextQuery.h"
#include "Query_base.h"
#include "WordQuery.h"
class 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(const string &s): q(new WordQuery(s)) {}//是为了WordQuery类准备 的
QueryResult eval(const TextQuery &t) const
{ return q->eval(t); }//虽然q是Query_base类的,但是由于其中存在纯虚函数,所以Query_base类型的//对象不存在,所以实际是指向四种继承类之一,eval函数也是继承类重定义之后的。
string rep() const {return q->rep();}
private:
Query(shared_ptr<Query_base> query): q(query) {}//是为了And、Or、Not类准备的
shared_ptr<Query_base> q;//由于Query_base是基类,所以他的智能指针可以指向其继承类,即后边四种
};
ostream& operator<<(ostream &os,const Query &query);//在这里只能声明,不能定义
#endif
Query.cpp文件
#include "Query.h"
#include "WordQuery.h"
ostream& operator<<(ostream &os,const Query &query)
{return os<<query.rep();}
WordQuery类
WordQuery.h文件
#ifndef WORDQUERY_H
#define WORDQUERY_H
#include "Query_base.h"
//#include "Query.h"
class Query;//同样为了避免互相调用,只声明,不引用头文件
class WordQuery: public Query_base
{
friend class Query;
WordQuery(const string &s):query_word(s) {}
QueryResult eval(const TextQuery &t) const
{ return t.query(query_word); }
string rep() const { return query_word; }
string query_word;
};
#endif
WordQuery.cpp文件,无实质内容
#include "WordQuery.h"
NotQuery类的实现
NotQuery.h文件
#ifndef NOTQUERY_H
#define NOTQUERY_H
#include "Query_base.h"
#include "Query.h"
class NotQuery: public Query_base
{
friend Query operator~(const Query &);
NotQuery(const Query &q): query(q) {}
string rep() const { return "~("+query.rep()+")";}
QueryResult eval(const TextQuery&) const;
Query query;
};
NotQuery.cpp文件
#include "NotQuery.h"
Query operator~(const Query &operand)
{
return shared_ptr<Query_base>(new NotQuery(operand));
}
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();//这里的begin、end以及get_file函数用到了我们之前自己定义的
auto sz=result.get_file()->size(); //QueryResult类中的函数
for (size_t n=0;n!=sz;++n)
{
if(beg==end||*beg!=n)
ret_lines->insert(n);
else if(beg!=end)
++beg;
}
return QueryResult(rep(),ret_lines,result.get_file());
}
#endif
BinaryQuery类的实现
BinaryQuery.h文件
#ifndef BINARYQUERY_H
#define BINARYQUERY_H
#include "Query_base.h"
#include "Query.h"
class BinaryQuery: public Query_base
{
protected:
BinaryQuery(const Query &left,const Query &right,string s):
lhs(left),rhs(right),opSym(s) {}
string rep() const { return "("+lhs.rep()+" "+opSym+" "+rhs.rep()+")";}
Query lhs,rhs;
string opSym;
};
#endif
BinaryQuery.cpp文件
#include "BinaryQuery.h"
AndQuery类的实现
AndQuery.h文件
#ifndef ANDQUERY_H
#define ANDQUERY_H
#include "BinaryQuery.h"
class AndQuery: public BinaryQuery
{
friend Query operator&(const Query&, const Query&);
AndQuery(const Query &left,const Query &right): BinaryQuery(left,right,"&") {}
QueryResult eval(const TextQuery&) const;
};
#endif
AndQuery.cpp文件
#include "AndQuery.h"
Query operator&(const Query &lhs,const Query &rhs)
{
return shared_ptr<Query_base>(new AndQuery(lhs,rhs));
}
QueryResult AndQuery::eval(const TextQuery& text) const
{
auto right = rhs.eval(text),left=lhs.eval(text);
auto ret_lines=make_shared<set<line_no>>();
set_intersection(left.begin(),left.end(),right.begin(),right.end(),
inserter(*ret_lines,ret_lines->begin()));
return QueryResult(rep(),ret_lines,left.get_file());
}
OrQuery类的实现
OrQuery.h文件
#ifndef ORQUERY_H
#define ORQUERY_H
#include "BinaryQuery.h"
class OrQuery: public BinaryQuery
{
friend Query operator|(const Query&, const Query&);
OrQuery(const Query &left,const Query &right): BinaryQuery(left,right,"|") {}
QueryResult eval(const TextQuery&) const;
};
#endif
OrQuery.cpp文件
#include "OrQuery.h"
Query operator|(const Query &lhs,const Query &rhs)
{
return shared_ptr<Query_base>(new OrQuery(lhs,rhs));
}
QueryResult OrQuery::eval(const TextQuery& text) const
{
auto right = rhs.eval(text),left=lhs.eval(text);
auto ret_lines=make_shared<set<line_no>>(left.begin(),left.end());
ret_lines->insert(right.begin(),right.end());
return QueryResult(rep(),ret_lines,left.get_file());
}
运行
好了,现在所有的源码都已经完成了,如果实在win10下,可以直接运行,如果实在Ubuntu下,还需要一个CMakeLists.txt
cmake_minimum_required(VERSION 2.0)
project(textmain)
add_executable(textmain main.cpp)
add_library(QueryResult QueryResult.cpp )
add_library(TextQuery TextQuery.cpp)
add_library(AndQuery AndQuery.cpp)
add_library(BinaryQuery BinaryQuery.cpp)
add_library(NotQuery NotQuery.cpp)
add_library(OrQuery OrQuery.cpp)
add_library(Query Query.cpp)
add_library(Query_base Query_base.cpp)
add_library(WordQuery WordQuery.cpp)
target_link_libraries(textmain QueryResult)
target_link_libraries(textmain TextQuery)
target_link_libraries(textmain AndQuery)
target_link_libraries(textmain BinaryQuery)
target_link_libraries(textmain NotQuery)
target_link_libraries(textmain OrQuery)
target_link_libraries(textmain Query)
target_link_libraries(textmain Query_base)
target_link_libraries(textmain WordQuery)
记得在写入一个名为use.txt的文件供查询
再有一个main.cpp文件
#include "WordQuery.h"
#include "NotQuery.h"
#include "OrQuery.h"
#include "AndQuery.h"
#include "Query.h"
int main()
{
ifstream use("../use.txt");
TextQuery file=use;
Query q = Query("fiery") & Query("bird") | Query("wind");
const auto results = q.eval(file);
cout << "Executing Query for: " << q << endl;
print(cout, results) << endl;
return 0;
}
之后cmake … ,make, ./textmain ,得到结果
往期
[1]https://mp.csdn.net/mdeditor/102644319#