1.本文所建立的代码是15章第9节的文本查询内容 希望大家可以看看功能基本实现
2.代码需求分析
Ⅰ进行文本查询 使用文件流对象进行文本解析
Ⅱ可以进行输入的逻辑语句 并进行解析
Ⅱ 将对应的行进行打印
3.实现手段
Ⅰ 进行文本解析进行智能指针保存 通过指针来进行保存和传递
Ⅱ 再对不同的逻辑对象的分析提供面向对象的手段进行 接口实现
Ⅱ 对输入的表达式进行来分析 ,实现如四则运算类型的分析
第一个文件定义 QuerResult 和 Textresult 类
进行的是按行打印 和对应的运行函数 rusnQueries
主要是通过 istringstream 来进行 行单词分解 保存 map<string,size_t> 中
文件的每一行使用 vector<string>保存 并使用 shared_ptr进行 动态保存
//QuerResult 和TextResult 此文件是 request.h 12.3节的内容就不详细介绍了
#ifndef REQUEST_H
#define REQUEST_H
#include<iostream>
#include<fstream>
#include<sstream>
#include<vector>
#include<set>
#include<map>
#include<string>
#include<memory>
#include<algorithm>
#include<iterator>
using std::cin;
using std::cout;
using std::shared_ptr;
using std::make_shared;
using std::vector;
using std::map;
using std::set;
using std::ifstream;
using std::string;
using std::istringstream;
//接收一个 ifstream对象 获得其文件流对象拷贝的指针和 对应单词的map
//返回QueryResult 的 query function
class QueryResult
{
public:
//这里与书上不同 因为在后续 类的设计中大量是分析所共享的指针 所以添加这些接口
QueryResult(const string &s,const shared_ptr<vector<string>>f ,shared_ptr<set<size_t>> line):
key_word(s),file_copy(f),line_no(line){}
set<size_t>::iterator begin() {return line_no->begin(); }
set<size_t>::iterator begin() const {return line_no->begin(); }
set<size_t>::iterator end() {return line_no->end(); }
set<size_t>::iterator end() const {return line_no->end(); }
shared_ptr<vector<string>> get_file(){return file_copy;}
shared_ptr<set<size_t>> line(){return line_no;}
size_t size() {return line_no->size();}
string word() {return key_word;}
~QueryResult(){}
private:
string key_word;
shared_ptr<vector<string>> file_copy;
shared_ptr<set<size_t>> line_no;//对应的行号
};
class TextQuery
{ friend class QueryResult;
friend void RunQueries(ifstream&);//打印结果的函数
public:
TextQuery(ifstream&);
QueryResult query(const string&) const ;
~TextQuery(){}
private:
shared_ptr<vector<string>> file;
map<string ,shared_ptr<set<size_t> > > search;
};
TextQuery::TextQuery(ifstream& Query_file):file(new vector<string>)
{
string line;
while(getline(Query_file,line))
{
file->push_back(line);
istringstream m(line);
size_t n= file->size()-1;//行号从0开始
string word;
while(m>>word) //进行单词分解
{
auto & lines=search[word];
if(!lines)
lines.reset(new set<size_t>);
lines->insert(n);
}
}
}
QueryResult
TextQuery::query(const string &key_word) const
{
auto s= search.find(key_word);//使用at 会在没有此单词时产生 异常
shared_ptr<set<size_t> > nodata( new set<size_t>);
if(s!=search.end())
return QueryResult(key_word,file,s->second);
else return QueryResult(key_word,file,nodata);
}
//保存着 关键字 对应的文件指针 和 set<size_t>的指针
void RunQueries(ifstream&tq)
{
TextQuery text(tq);
cout<<"enther the word to serach or q to quit";
string s;
if(cin>>s||s!="q")
{
auto mo= text.query(s);
cout<<s<<"出现的了"<<mo.size()<<"次";
for(auto i=mo.begin();i!=mo.end();++i)
{
cout<<*i<<"行"<<(*mo.get_file())[*i]<<std::endl;
}
}
}
#endif // !REQUEST_H
这里主要使用面向对象的思想方法 进行 动态绑定 面对不同的对象 和运算符时 调用 不同的Query类型的eval查询操作
#ifndef REQUEST_H
#include"request.h"
#define REQUEST_H
// 此文件 Query和Query_base派生出的 Word_Query Or_Quer Not_Query And_Query
//单个运算符对象使用 继承基类Query_base
//Query_base 有两个功能 1...eval执行对应的查询操作 接收一个TextQuery 返回Query_result
//2...rep() 通过调用eval对象获得匹配的QueryResult 打印自身的运算 即 Not_query 打印出 ~keyword;
class Query;
class Query_base
{ friend class Query;
protected:
virtual~Query_base() {}
private:
virtual QueryResult eval(const TextQuery&) const =0;
virtual string rep () const =0;
};
//class Query通过 重载运算符来实现不同的 保存指向Query_base的指针
class Query
{
friend Query operator |(const Query&,const Query&);
friend Query operator &(const Query&, const Query&);
friend Query operator ~(const Query&);
public:
Query() = default;
Query(const string & sought);
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;
};
std::ostream & operator<<(std::ostream &os,const Query &query)
{
return os<<query.rep();
}
//与Query产生的对象 WordQuery
class WordQuery:public Query_base//如果缺少public标识符则为 private继承
{
friend class Query;
WordQuery(const string &t):sought(t){}
QueryResult eval(const TextQuery&t1) const { return t1.query(sought);}// error out of range
string rep() const {return sought;}
string sought;
};
inline Query::Query(const string & t):q(new WordQuery (t)){}
//NotQuery是 形成独立的类所以 产生是另一个对象 所以为私有
//作用是利用指针产生动态绑定 1.. 定义自己的eval操作
//2... rep() 函数在原来的基础上返回自己的运算符
class NotQuery: public Query_base
{
friend Query operator ~(const Query&);
private:
NotQuery( const Query&q):query(q){}
QueryResult eval(const TextQuery&t)const ;
string rep() const {return "~("+query.rep()+")";}
Query query;
};
inline Query operator ~(const Query& t)
{
return shared_ptr<Query_base>(new NotQuery(t));
}
//二元运算符的虚基类
class BinaryQuery :public Query_base
{
protected:
//因为传入是const char[] 所以为 string 而不是string&
BinaryQuery(const Query&l,string m,const Query&r):lhs(l),opSym(m),rhs(r){}
//virtual QueryResult eval() const =0; 不定义eval
//打印符号
virtual string rep() const {return "("+lhs.rep()+" "+opSym+rhs.rep()+")";}
Query lhs,rhs;//左右两个运算对象
string opSym;//运算符
};
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 ;
};
inline Query operator &(const Query&r,const Query&t)
{
//智能指针的参数应该是Query_base 指向基类对象
return shared_ptr<Query_base>(new AndQuery(r,t));
}
class OrQuery:public BinaryQuery
{
friend class QueryResult;
friend Query operator |(const Query&,const Query&);
OrQuery(const Query &left,const Query &right):BinaryQuery(left,"&",right){}
QueryResult eval(const TextQuery& )const ;
};
inline Query operator |(const Query&r,const Query&t)
{
return shared_ptr<Query_base>(new OrQuery(r,t));
}
//各eval 函数的定义
QueryResult
NotQuery::eval(const TextQuery&t)const
{
auto result=query.eval(t);//获得QueryResult对象
shared_ptr<set<size_t> >line_result(new set<size_t>);
auto be=result.begin(); auto en=result.end();
for(size_t n=0;n!=result.get_file()->size();++n)
{
//因为set是自动排序的所以 从零开始比较 如果不是加入到 line_result中去
if(be==en||*be!=n)
line_result->insert(n);
else ++be;
}
return QueryResult(rep(),result.get_file(),line_result);
}
QueryResult
OrQuery::eval(const TextQuery&t)const
{
auto right=rhs.eval(t),left=lhs.eval(t);
shared_ptr<set<size_t>> line_result(new set<size_t>);
//并集
line_result->insert(right.begin(),right.end());
line_result->insert(left.begin(),left.end());
//注意这里返回是自身的rep()符号显示操作
return QueryResult(rep(),left.get_file(),line_result);
}
QueryResult
AndQuery::eval(const TextQuery&t)const
{
auto left=lhs.eval(t),right=rhs.eval(t);
shared_ptr<set<size_t>> line_result(new set<size_t>);
//交集
for(auto a=left.begin(),b=right.end(); a!=left.end()&&b!=right.end() ; ++a,++b)
{
if(*a==*b) line_result->insert(*a);
}
//或者使用 标准算法的 来合并
//std::_Set_intersection(left.begin(),left.end(),right.begin(),right.end(),std::(*line_result,line_result->begin()));
return QueryResult(rep(),left.get_file(),line_result);
}
void print(QueryResult&result)
{
cout<<result.word()<<"出现"<<result.line()->size()<<"次"<<std::endl;
for(auto num:*result.line())
{
cout<<"line "<<num+1<<std::endl;//避免从0开始的行号产生错误
cout<<(*(result.get_file()))[num]<<std::endl;
}
}
//对于runQueries的改造 直接进行 打印 避免分析语句
此行代码是点击打开链接 这位仁兄提供的思路
//有点偷懒 所以 我重新写了打印部分 实现逻辑语句的想法
void runQueries(ifstream& file)
{
TextQuery tq(file);
string s1,s2,s3;
cout<<"请输入三个单词进行组合查询"<<std::endl;
cin>>s1>>s2>>s3;
Query Q1=Query (s1)&Query(s2)&Query(s3);
Query Q2=Query (s1)|Query(s2)|Query(s3);
Query Q3=Query (s1)|Query(s2)&Query(s3);
Query Q4 = ~Query(s1);
print(Q1.eval(tq));
print(Q2.eval(tq));
print(Q3.eval(tq));
print(Q4.eval(tq));
}
#endif
此部分是进行逻辑运算语句的分析 同时 给出关于四则运算的解析方案进行对照
#include"Query_base.h"
#include<stack>
using std::stack;
//设计函数进行对于表达式解析
//进行改变成后缀表达式 进行类似的四则运算
unsigned int is_oper(const char c)
{
if ((c == '+ ') || (c == '- ')) return 2;
else if ((c == '* ') || (c == '/ ')) return 3;
else if ((c == '( ') || (c == ') ')) return 1;
else if (c >= '0'&&c <= '9') return 4;
else return 0;
}
unsigned int is_oper_string(string& c)
{
if ((c =="~")) return 2;
else if ((c == "&"||(c == "|"))) return 3;
else if ((c == "(") || (c == ")")) return 1;
else return 4;
}
//此处是四则运算转化为后缀表达式的 实现过程
string Mid_expresstion(stack<char>&rhs, string& s1)
{
string result;
for (auto &s : s1)
{
auto check = is_oper(s);//检查结果
if ((check == 4)){result.push_back(s); continue;}//数字直接进入表达式中
if (rhs.empty() ||s=='(') { rhs.push(s); continue; }
if (s == ')')
{
while (rhs.top()!='(')
{ result.push_back(rhs.top()); rhs.pop();}
rhs.pop();// ( 不保存
continue;
}
else
{
/*
若为 除括号外的其他运算符, 当其优先级高于除'('以外的栈顶运算符时,直接入栈。
否则从栈顶开始,依次弹出比当前处理的运算符优先级高和优先级相等的运算符,
直到一个比它优先级低的或者遇到了一个左括号为止。
*/
//error 错误deque iterator not dereferencable
while (check <=is_oper(rhs.top())&&(rhs.top()!='('))
{
result.push_back(rhs.top()); rhs.pop();
if (rhs.empty()) break;
} rhs.push(s);
}
}
while (!rhs.empty())
{
result.push_back(rhs.top());
rhs.pop();
} return result;
}
/*
例如在后缀表达式31+2*2/中:
第一步遇到+ 计算结果 42*2/
第一步遇到* 计算结果 82/
第一步遇到/ 计算结果 4*/
//此次是四则运算的计算过程
double calculate(string &s)
{
stack<double>ppx;
double c = 0.0, b = 0.0, result = 0.0;
for (unsigned int a =0; a <= s.size()-1; ++a)//将'/0'除去
{
if (s[a] >= '0'&&s[a] <= '9')
{
ppx.push(s[a]-'0');
}
else
{
switch (s[a])
{
case '*': {c = ppx.top(); ppx.pop(); b = ppx.top(); ppx.pop(); result = c * b; ppx.push(result); }break;
case '+': {c = ppx.top(); ppx.pop(); b = ppx.top(); ppx.pop(); result = c + b; ppx.push(result); }break;
case '-': {c = ppx.top(); ppx.pop(); b = ppx.top(); ppx.pop(); result = b- c; ppx.push(result); } break;
case '/': {c = ppx.top(); ppx.pop(); b = ppx.top(); ppx.pop(); result = b / c; ppx.push(result); } break;
default:
break;
}
}
}return ppx.top();
}
//使用后缀表达式的方式进行逻辑表达进行解析
vector<string> Mid_expresstion_string(stack<string>&rhs,string &s)
{ //~ 2 & | 3 () 1 else 4
istringstream ppx(s);
string word;
vector<string> result;
while (ppx >>word)
{
auto check = is_oper_string(word);//检查结果
if ((check == 4)) { result.push_back(word); continue; }//单词直接进入表达式中
if (rhs.empty() || word =="(") { rhs.push(word); continue; }
if (word ==")")
{
while (rhs.top() != "(")
{
result.push_back(rhs.top()); rhs.pop();
}
rhs.pop();// ( 不保存
continue;
}
else
{
/*
若为 除括号外的其他运算符, 当其优先级高于除'('以外的栈顶运算符时,直接入栈。
否则从栈顶开始,依次弹出比当前处理的运算符优先级高和优先级相等的运算符,
直到一个比它优先级低的或者遇到了一个左括号为止。
*/
//error 错误deque iterator not dereferencable
while (check <= is_oper_string(rhs.top()) && (rhs.top() != "("))
{
result.push_back(rhs.top()); rhs.pop();
if (rhs.empty()) break;
} rhs.push(word);
}
}
while (!rhs.empty())
{
result.push_back(rhs.top());
rhs.pop();
}
return result;
}
//逻辑语句的分析与四则运算类似 使用stack 来存储 生成的不同类型的Query
Query calculate_string(vector<string> &s)
{ // ~ 2 & | 3 () 1 else 4
stack<Query> ppx_text;
Query c, b, result;
for (unsigned int a = 0; a < s.size(); ++a)
{
auto brevity = is_oper_string(s[a]);
if(brevity==4)
{
ppx_text.push(Query(s[a]));
}
switch (brevity)
{
case 2: { b = ~Query(ppx_text.top()); ppx_text.pop(); ppx_text.push(b); } break;
case 3: if (s[a] == "&")
{
b=Query(ppx_text.top()); ppx_text.pop();c= Query(ppx_text.top()); ppx_text.pop();
ppx_text.push(c&b);
}
else
{
b = Query(ppx_text.top()); ppx_text.pop(); c = Query(ppx_text.top()); ppx_text.pop();
ppx_text.push(c | b);
}break;
default:break;
}
}return ppx_text.top();
}
int main()
{
//这里用的文件是 书上的文件
ifstream in("storyDatafile");
//runQueries(in);
string s = "~ ( Alice & ppx )";
cout << "请输入需要的逻辑语句" << "如~ ( Alice & ppx )" << "请打空格" << std::endl;
std::getline(cin, s); //cin >> s;错误有空格 会导致字符串输入不完整
stack<string>ppx;
Query te1=calculate_string( Mid_expresstion_string(ppx, s));
print(te1.eval(in));
system("pause");
return 0;
}