您的位置:首页 > 编程语言 > C语言/C++

文本查询程序--摘自c++primer

2013-07-08 11:37 267 查看
本文总结摘录了《c++ primer》中的一个大例子---文本查询程序。主要目的在于学习大师的编程规范。

程序要求:读取用户指定的任意文本文件,然后允许用户从该文件中查找单词。查询的结果是该单词出现的次数,并列出每次出现所在的行。如果某单词在同一行中多次出现,程序将只显示该行一次,行号按升序显示。

分析:

输入为文件名;
必须将一行作为一个整体,由行号输出所在行;
必须每一行都分解为各个单词,并记录单词所在行,对于单词的查询返回所有所在行;
此处主要考察容器的使用;

求解:

使用vector来存储整行,vector中一个元素就是一行;
使用map来以单词为关键字,以所在行的集合为卫星数据;

下面是所包含的的头文件:

#include<utility>
#include<set>
#include<map>
#include<vector>
#include<string>
#include<iostream>
#include<fstream>
#include<sstream>
using namespace std;


下面是主要类TextQuery:

class TextQuery
{
public:
//typedef to make declarations easier
typedef  std::vector<std::string>::size_type  line_no;
typedef std::set<line_no>::const_iterator  set_it;

/* interface:
* read_file() builds internal data structures  for the given file
* run_query() finds the given word and returns "set" of lines on which it appears
* text_line()  return a requested line from the input file
*/

void read_file(std::ifstream&  is)
{  store_file(is);  build_map();}
std::pair<set_it,set_it>  run_query(const std::string& )  const;
std::string text_line(line_no) const;

private:
//utility functions used by read_file()
void store_file(std::ifstream& );  //store input file
void build_map();  //associated each word with a set of line numbers;

std::vector<std::string> lines_of_text;  //remember the whole input file
std::map<std::string, std::set<line_no> > word_map;
};


下面是辅助函数make_plural和open_file:

/*此函数提供一个计数器,一个单词word和单词结束符ending,
当计数器值大于1时,返回单词的复数版本*/
std::string make_plural(size_t ctr,const std::string& word, const std::string& ending)
{
return (ctr==1)?word :word+ending;
}

//打开给定文件
std::ifstream& open_file(std::ifstream& in,const std::string& file)
{
in.close();//close in case it was already open
in.clear();//clear any existing errors;
in.open(file.c_str());
return in;
}


下面是类中的各个函数实现,此处run_query的实现采用的是作者的第二种建议,返回一对迭代器:

//第一步:存储输入文件
void TextQuery::store_file(ifstream&  is)
{
string textline;
while( getline(is,textline) )
lines_of_text.push_back(textline);
}

//第二步:建立单词map容器

void TextQuery::build_map()
{
for(line_no line_num=0;line_num!=lines_of_text.size();++line_num){
istringstream line(lines_of_text[line_num]);
string word;
while(line>>word){
//add this line number to the set;
//其中word_map[word]是一个集合,然后insert
word_map[word].insert(line_num);
}
}
}

//第三步:支持查询
//返回set的一对迭代器
pair<TextQuery::set_it,TextQuery::set_it>
TextQuery::run_query(const string& query_word) const
{
//此处需要注意不能用下标操作直接查询,以防止插入不存在的元素
map<string,set<line_no> >::const_iterator loc=word_map.find(query_word);
return make_pair((loc->second).begin(),(loc->second).end());
}

//第四步: 输出结果
void print_results( pair<TextQuery::set_it,TextQuery::set_it>& its,
const string& sought,const TextQuery& file)
{
typedef set<TextQuery::line_no> line_nums;
line_nums::size_type size=0;
for(TextQuery::set_it beg=its.first;beg!=its.second;++beg)
++size;
cout<<"\n"<<sought<<" occurs "<<size<<"  "<<make_plural(size,"time","s")<<endl;

line_nums::const_iterator it=its.first;
for(;it!=its.second;++it){
cout<<"\t(line "<<(*it)+1<<")"<<file.text_line(*it)<<endl;
}
}
string TextQuery::text_line(line_no line) const
{
if(line<lines_of_text.size())
return lines_of_text[line];
throw std::out_of_range("Line number out of range");
}


测试程序如下:

int main()
{
ifstream infile;
string filename;
if(!(cin>>filename) || !open_file(infile,filename)){
cerr<<"NO input file!"<<endl;
return EXIT_FAILURE;
}
TextQuery tq;
tq.read_file(infile);
while(true){
cout<<"enter word to look for, or q to quit: ";
string s;cin>>s;
if(!cin || s=="q") break;
pair<TextQuery::set_it,TextQuery::set_it> its=tq.run_query(s);
print_results(its,s,tq);
}
return 0;
}


扩展上面的文本查询程序,要求:

查找单个单词的查询,按升序显示所有包含该单词的行;
“非”查询,使用~操作符,显示所有不匹配行;
“或”查询,使用|操作符,显示与两个查询条件中任意一个匹配的所有行;
“与”查询,使用&操作符,显示与两个查询条件都匹配的所有行;
可以组合这些操作符;

设计类如下:



我们使用时:

Query q=Query(“ ”) & Query(“ ”)|Query()

这个使用方式告诉我们,用户级代码将不能直接使用我们的继承层次,我们只能定义一个名为Query的句柄类,来隐藏继承层次。用户代码将根据句柄执行,用户代码只能间接操纵Query_base对象。

以Query q=Query("fiery") & Query("bird") |Query("wind")为例:



生成10个对象:5个Query_base对象及其相关联的句柄。

此处直接截取书中代码:

// private, abstract class acts as a base class for concrete query types
class Query_base {
friend class Query;
protected:
typedef TextQuery::line_no line_no;
virtual ~Query_base() { }
private:
// eval returns the |set| of lines that this Query matches
virtual std::set<line_no>
eval(const TextQuery&) const = 0;
// display prints the query
virtual std::ostream&
display(std::ostream& = std::cout) const = 0;
};


Query句柄类:

// handle class to manage the Query_base inheritance hierarchy
class Query {
// these operators need access to the Query_base* constructor
friend Query operator~(const Query &);
friend Query operator|(const Query&, const Query&);
friend Query operator&(const Query&, const Query&);
public:
Query(const std::string&); // builds a new WordQuery
// copy control to manage pointers and use counting
Query(const Query &c): q(c.q), use(c.use) { ++*use; }
~Query() { decr_use(); }
Query& operator=(const Query&);
// interface functions: will call corresponding Query_base operations
std::set<TextQuery::line_no>
eval(const TextQuery &t) const { return q->eval(t); }
std::ostream &display(std::ostream &os) const
{ return q->display(os); }
private:
Query(Query_base *query): q(query),
use(new std::size_t(1)) { }
Query_base *q;
std::size_t *use;
void decr_use()
{ if (--*use == 0) { delete q; delete use; } }
};


重载操作符:

inline Query operator&(const Query &lhs, const Query &rhs)
{
return new AndQuery(lhs, rhs);
}
inline Query operator|(const Query &lhs, const Query &rhs)
{
return new OrQuery(lhs, rhs);
}
inline Query operator~(const Query &oper)
{
return new NotQuery(oper);
}


输出操作符:

inline std::ostream&
operator<<(std::ostream &os, const Query &q)
{
return q.display(os);
}


WordQuery类:

class WordQuery: public Query_base {
friend class Query; // Query uses the WordQuery constructor
WordQuery(const std::string &s): query_word(s) { }
// concrete class: WordQuery defines all inherited pure virtual functions
std::set<line_no> eval(const TextQuery &t) const
{ return t.run_query(query_word); }
std::ostream& display (std::ostream &os) const
{ return os << query_word; }
std::string query_word; // word for which to search
};


NotQuery类:

class NotQuery: public Query_base {
friend Query operator~(const Query &);
NotQuery(Query q): query(q) { }
// concrete class: NotQuery defines all inherited pure virtual functions
std::set<line_no> eval(const TextQuery&) const;
std::ostream& display(std::ostream &os) const
{ return os << "~(" << query << ")"; }
const Query query;
};

set<TextQuery::line_no>
NotQuery::eval(const TextQuery& file) const
{
// virtual call through the Query handle to eval
set<TextQuery::line_no> has_val = query.eval(file);
set<line_no> ret_lines;
// for each line in the input file, check whether that line is in has_val
// if not, add that line number to ret_lines
for (TextQuery::line_no n = 0; n != file.size(); ++n)
if (has_val.find(n) == has_val.end())
ret_lines.insert(n);
return ret_lines;
}


Binary纯虚类:

class BinaryQuery: public Query_base {
protected:
BinaryQuery(Query left, Query right, std::string op):
lhs(left), rhs(right), oper(op) { }
// abstract class: BinaryQuery doesn't define eval
std::ostream& display(std::ostream &os) const
{ return os << "(" << lhs << " " << oper << " "
<< rhs << ")"; }
const Query lhs, rhs; // right- and left-hand operands
const std::string oper; // name of the operator
};


OrQuery类:

class OrQuery: public BinaryQuery
{
friend Query operator|(const Query&, const Query&);
OrQuery(Query left, Query right):
BinaryQuery(left, right, "|") { }
// concrete class: OrQuery inherits display and defines remaining pure virtual
std::set<line_no> eval(const TextQuery&) const;
};

set<TextQuery::line_no>
OrQuery::eval(const TextQuery& file) const
{
// virtual calls through the Query handle to get result sets for the operands
set<line_no> right = rhs.eval(file),
ret_lines = lhs.eval(file); // destination to hold results
// inserts the lines from right that aren't already in ret_lines
ret_lines.insert(right.begin(), right.end());

return ret_lines;
}


AndQuery类:

class AndQuery: public BinaryQuery {
friend Query operator&(const Query&, const Query&);
AndQuery (Query left, Query right):
BinaryQuery(left, right, "&") { }
// concrete class: And Query inherits display and defines remaining pure virtual
std::set<line_no> eval(const TextQuery&) const;
};

set<TextQuery::line_no>
AndQuery::eval(const TextQuery& file) const
{
// virtual calls through the Query handle to get result sets for the operands
set<line_no> left = lhs.eval(file),
right = rhs.eval(file);
set<line_no> ret_lines; // destination to hold results
// writes intersection of two ranges to a destination iterator
// destination iterator in this call adds elements to ret
set_intersection(left.begin(), left.end(),
right.begin(), right.end(),
inserter(ret_lines, ret_lines.begin()));
return ret_lines;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐