C++primer( 第五版 )第15章的Query文本查询功能的实现

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#

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值